<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Speedyk-005</title>
    <description>The latest articles on DEV Community by Speedyk-005 (@speed_k_7e1b449706e59e433).</description>
    <link>https://dev.to/speed_k_7e1b449706e59e433</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2273923%2F3adfb205-24cf-4e87-ba59-30c6e0beb6ac.jpg</url>
      <title>DEV Community: Speedyk-005</title>
      <link>https://dev.to/speed_k_7e1b449706e59e433</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/speed_k_7e1b449706e59e433"/>
    <language>en</language>
    <item>
      <title>Don't delete your old code. It might be someone's next starting point.</title>
      <dc:creator>Speedyk-005</dc:creator>
      <pubDate>Wed, 29 Jul 2026 05:52:37 +0000</pubDate>
      <link>https://dev.to/speed_k_7e1b449706e59e433/dont-delete-your-old-code-it-might-be-someones-next-starting-point-30nc</link>
      <guid>https://dev.to/speed_k_7e1b449706e59e433/dont-delete-your-old-code-it-might-be-someones-next-starting-point-30nc</guid>
      <description>&lt;p&gt;Every developer has code they'll probably never touch again.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The half-finished CLI from a weekend.&lt;/li&gt;
&lt;li&gt;The tutorial project that stopped halfway through.&lt;/li&gt;
&lt;li&gt;That weird experiment that actually taught you something.&lt;/li&gt;
&lt;li&gt;The utility script that's ugly but gets the job done.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most of that code just sits on a hard drive until it's forgotten.&lt;/p&gt;

&lt;p&gt;I started The Open CodeYard, a GitHub repository for unfinished, experimental, and abandoned projects that are still worth preserving. Not because they're perfect, but because ideas are often more valuable than polished code.&lt;/p&gt;

&lt;p&gt;The structure is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"scraps/" for random leftovers.&lt;/li&gt;
&lt;li&gt;"prototypes/" for intentional experiments.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each project gets a small README explaining what it does and what state it's in.&lt;/p&gt;

&lt;p&gt;A few ground rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No clean-code police.&lt;/li&gt;
&lt;li&gt;Messy code is expected.&lt;/li&gt;
&lt;li&gt;Abandoned projects are welcome.&lt;/li&gt;
&lt;li&gt;If you're thinking, "my code isn't good enough," you're exactly who this repo is for.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Right now it's just my own projects in there, including a few C# pathfinding algorithms, a speech rate estimator, and some Python utilities. I'm hoping it grows into a community archive where forgotten code can still teach, inspire, or even get revived by someone else.&lt;/p&gt;

&lt;p&gt;If you've got code collecting digital dust, I'd love to see it rescued instead.&lt;/p&gt;

&lt;p&gt;Repository:&lt;br&gt;
&lt;a href="https://github.com/speedyk-005/TheOpenCodeYard" rel="noopener noreferrer"&gt;https://github.com/speedyk-005/TheOpenCodeYard&lt;/a&gt;&lt;/p&gt;

</description>
      <category>code</category>
      <category>coding</category>
      <category>programming</category>
      <category>github</category>
    </item>
    <item>
      <title>Replacing spaCy’s Sentencizer with yasbd: From 55.4% to 98.9% Accuracy</title>
      <dc:creator>Speedyk-005</dc:creator>
      <pubDate>Mon, 27 Jul 2026 23:05:04 +0000</pubDate>
      <link>https://dev.to/speed_k_7e1b449706e59e433/replacing-spacys-sentencizer-with-yasbd-from-554-to-989-accuracy-5f0d</link>
      <guid>https://dev.to/speed_k_7e1b449706e59e433/replacing-spacys-sentencizer-with-yasbd-from-554-to-989-accuracy-5f0d</guid>
      <description>&lt;p&gt;Sentence segmentation—splitting text into individual sentences—is a foundational step in most NLP pipelines. It seems simple, but as anyone who has worked with real-world text knows, the details matter. Abbreviations like Dr., URLs, ellipsis, and complex punctuation can trip up even well-intentioned rule-based systems.&lt;/p&gt;

&lt;p&gt;spaCy's built-in Sentencizer is a fast, lightweight, rule-based component designed for this task. However, by design, it makes a trade-off: speed and simplicity over accuracy on edge cases. For many production applications, this trade-off leads to catastrophic downstream errors.&lt;/p&gt;

&lt;p&gt;Enter yasbd (Yet Another Sentence Boundary Detector). It's a pure-Python, rule-based SBD that can be integrated as a drop-in spaCy component, and it achieves a remarkable 98.9% accuracy on a challenging English golden benchmark, compared to spaCy's Sentencizer which scores only 55.4% [1].&lt;/p&gt;

&lt;p&gt;This post will walk you through why spaCy's default segmenter falls short, and how you can easily upgrade your pipeline with yasbd to achieve near-perfect sentence boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why spaCy's Sentencizer is Intentionally Lightweight
&lt;/h2&gt;

&lt;p&gt;The Sentencizer is a non-trainable pipeline component. Reading its source code reveals its core logic is only a few dozen lines [2]:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Mark the first token of the document as a sentence start.&lt;/li&gt;
&lt;li&gt;Walk through every token in the document.&lt;/li&gt;
&lt;li&gt;If a token is in punct_chars (., ?, !, 。, etc.), remember that a sentence may end here.&lt;/li&gt;
&lt;li&gt;Skip over any following punctuation.&lt;/li&gt;
&lt;li&gt;As soon as the next token is not punctuation, mark it as the beginning of a new sentence.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In simplified pseudocode, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="n"&gt;seen_period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;span class="n"&gt;doc_guesses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;punct_chars&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;seen_period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;seen_period&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;doc_guesses&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
        &lt;span class="n"&gt;seen_period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. It performs no abbreviation list lookup, no context analysis, and no handling of quotes, parentheses, or URLs. As the spaCy documentation itself notes, it's a component for when you "don't require the dependency parse" and want a "simpler, rule-based strategy" [3]. It was never designed to be a robust sentence splitter for messy, real-world text.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wait, Doesn't spaCy Handle Abbreviations?
&lt;/h2&gt;

&lt;p&gt;You might be wondering: "But spaCy handles Dr. just fine, doesn't it?" This is a sharp observation, and it gets to an important distinction in how spaCy processes text.&lt;/p&gt;

&lt;p&gt;The Sentencizer itself has no abbreviation awareness, but spaCy's Tokenizer runs first and applies hard-coded rules called Tokenizer Exceptions for common patterns like Dr., Mr., Ms., and U.K. [5, 6].&lt;/p&gt;

&lt;p&gt;Here's what happens:&lt;/p&gt;

&lt;p&gt;· Dr.: The Tokenizer keeps it as a single token ['Dr.']. The Sentencizer sees this as one unit and often bypasses the boundary check.&lt;br&gt;
· A.: The Tokenizer splits this into &lt;a href="https://dev.tostandard%20behavior%20for%20initials"&gt;'A', '.'&lt;/a&gt;. The Sentencizer now sees a standalone period token and forces a sentence split.&lt;br&gt;
· M.D.: No built-in exception exists, so the Tokenizer splits it into ['M', '.', 'D', '.']. The Sentencizer sees multiple standalone periods and fragments the text.&lt;/p&gt;

&lt;p&gt;This is why the Sentencizer works on simple cases like "Dr. Smith" but collapses on compound abbreviations, citations, or text with multiple initials. The tokenizer provides some protection, but it's incomplete and inconsistent [4, 6].&lt;/p&gt;

&lt;p&gt;A GitHub issue from 2021 highlights this limitation: a user pointed out that for Polish, the Sentencizer doesn't handle common abbreviations like dr., prof., or inż., making it effectively broken for that language [6]. The response from the spaCy team acknowledged that adding default exceptions for all languages is challenging, as the tokenizer defaults follow the guidelines of specific training corpora [7]. This means that for the vast majority of the 75+ languages it claims to support, the Sentencizer has minimal abbreviation awareness.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Benchmark Gap: 55.4% vs. 98.9%
&lt;/h2&gt;

&lt;p&gt;The performance difference becomes stark when you test these tools on a diverse set of challenging English texts. The yasbd benchmark suite includes a golden dataset of 92 English edge cases, expanded from the original 48 cases used by pysbd. These cases are specifically designed to break naive splitters and include abbreviation chains, URLs, quotes, ellipsis, and academic citations [1].&lt;/p&gt;

&lt;p&gt;The results from the yasbd benchmarks are clear [1]:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Score on 92 Edge Cases&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;yasbd&lt;/td&gt;
&lt;td&gt;91/92 (98.9%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pysbd&lt;/td&gt;
&lt;td&gt;77/92 (83.7%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sentencex&lt;/td&gt;
&lt;td&gt;76/92 (82.6%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;blingfire&lt;/td&gt;
&lt;td&gt;75/92 (81.5%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sentsplit&lt;/td&gt;
&lt;td&gt;61/92 (66.3%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;sentence-splitter&lt;/td&gt;
&lt;td&gt;60/92 (65.2%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;nupunkt&lt;/td&gt;
&lt;td&gt;59/92 (64.1%)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;spacy-sentencizer&lt;/td&gt;
&lt;td&gt;51/92 (55.4%)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This puts spacy-sentencizer at the very bottom of the list. It fails on nearly half of the test cases, primarily because it has no concept of abbreviations and will blindly split on any period, regardless of context.&lt;/p&gt;
&lt;h2&gt;
  
  
  How to Install yasbd-lib
&lt;/h2&gt;

&lt;p&gt;The installation is straightforward via pip. Note that yasbd-lib is a pure-Python library with no native dependencies, making it easy to install in any environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;yasbd-lib &lt;span class="nt"&gt;-U&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrating yasbd as a spaCy Component
&lt;/h2&gt;

&lt;p&gt;This is where the power of yasbd really shines for spaCy users. It provides a simple one-function registration API, allowing you to replace the default sentencizer with just a few lines of code [8].&lt;/p&gt;

&lt;p&gt;spaCy is not a dependency of yasbd. You will need to install it separately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;spacy &lt;span class="nt"&gt;-U&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have both libraries, the integration is seamless. The key is to add the yasbd component first in your pipeline. This ensures its sentence boundaries are set early and can be used by subsequent components like the parser or NER, rather than being overwritten.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;yasbd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;register_spacy_component&lt;/span&gt;

&lt;span class="c1"&gt;# 1. Register the component. This tells spaCy about the "yasbd" factory.
&lt;/span&gt;&lt;span class="nf"&gt;register_spacy_component&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Requires spaCy v3+
&lt;/span&gt;
&lt;span class="c1"&gt;# 2. Create a blank English pipeline.
&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 3. Add the yasbd component FIRST.
#    `first=True` ensures it runs before the parser, so its boundaries are preserved.
&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yasbd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lang&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# 4. Process your text.
&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Dr. Smith arrived. He was late.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# 5. Access the beautifully segmented sentences.
&lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Output:
# Dr. Smith arrived.
# He was late.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;The yasbd component is highly configurable. You can set the language, enable verbose logging, or even use automatic language detection [8].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Language can be inherited from the pipeline's language
&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yasbd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# `lang` defaults to `nlp.lang`
&lt;/span&gt;
&lt;span class="c1"&gt;# Or, use auto-detection (slower, but useful for mixed-language text)
&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yasbd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lang&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# Or, configure it at runtime
&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yasbd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fr&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;# Switch to French rules
&lt;/span&gt;&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verbose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
&lt;span class="n"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;preserve_quote_and_paren&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;  &lt;span class="c1"&gt;# Disable quote/parenthesis protection
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Before and After Examples
&lt;/h2&gt;

&lt;p&gt;Let's see the difference in action on a piece of text designed to break the Sentencizer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;yasbd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;register_spacy_component&lt;/span&gt;

&lt;span class="nf"&gt;register_spacy_component&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# --- Pipeline with spaCy's default sentencizer ---
&lt;/span&gt;&lt;span class="n"&gt;nlp_default&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;nlp_default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sentencizer&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# --- Pipeline with yasbd ---
&lt;/span&gt;&lt;span class="n"&gt;nlp_yasbd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;blank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;nlp_yasbd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;yasbd&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;lang&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"""&lt;/span&gt;&lt;span class="s"&gt;Dr. Patel A. (M.D., Ph.D.), can corroborate my claim.
You can reach me at j.doe@example.com or visit https://www.example.com/page?ref=1.
As Smith et al. (2021) noted: &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The implications are far-reaching.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; However, critics disagree.&lt;/span&gt;&lt;span class="sh"&gt;"""&lt;/span&gt;

&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;--- spaCy Sentencizer ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;nlp_default&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;sents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Output:
# 'Dr. Patel A. (M.D., Ph.D.), can corroborate my claim.'
# '
# You can reach me at j.doe@example.com or visit https://www.example.com/page?ref=1.'
# '
# As Smith et al. ('
# '2021) noted: "The implications are far-reaching."'
# 'However, critics disagree.'
#
# Notice: Newlines become sentence starts, and "2021" becomes its own fragment.
&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;--- yasbd ---&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;nlp_yasbd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;sents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"'&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# Output:
# 'Dr. Patel A. (M.D., Ph.D.), can corroborate my claim.
# '
# 'You can reach me at j.doe@example.com or visit https://www.example.com/page?ref=1.
# '
# 'As Smith et al. (2021) noted: "The implications are far-reaching."'
# 'However, critics disagree.'
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Sentencizer output is a mess:&lt;/p&gt;

&lt;p&gt;· It splits on newline characters, creating empty sentence fragments.&lt;br&gt;
· It shatters the citation "(2021)" into its own sentence.&lt;br&gt;
· It completely fails to recognize compound abbreviations like M.D., Ph.D..&lt;/p&gt;

&lt;p&gt;yasbd, with its comprehensive two-pass system—first identifying candidate boundaries, then surgically removing false positives using abbreviation lists and context rules—handles the entire text flawlessly [8].&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Sentence segmentation is a critical first step in any NLP pipeline. While spaCy's default Sentencizer is a convenient, lightweight option, its simplistic, period-based logic makes it highly inaccurate on real-world text. While the Tokenizer provides some protection for common abbreviations like Dr., this protection is incomplete and fails on compound abbreviations, initials, and non-English text [5, 6, 7]. The benchmarks show it failing on nearly half of the tested edge cases [1].&lt;/p&gt;

&lt;p&gt;yasbd offers a compelling alternative. It is a high-accuracy, rule-based SBD that can be integrated as a spaCy component with just a few lines of code, providing a massive leap in accuracy from 55.4% to 98.9% [8].&lt;/p&gt;

&lt;p&gt;For any spaCy user who cares about the quality of their sentence boundaries—and the downstream tasks that depend on them—replacing the sentencizer with yasbd is an easy and highly effective upgrade.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;[1] yasbd-lib benchmarks. &lt;a href="https://github.com/speedyk-005/yasbd-lib/blob/main/benchmarks/README.md" rel="noopener noreferrer"&gt;https://github.com/speedyk-005/yasbd-lib/blob/main/benchmarks/README.md&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[2] spaCy Sentencizer source code. &lt;a href="https://github.com/explosion/spaCy/blob/master/spacy/pipeline/sentencizer.pyx" rel="noopener noreferrer"&gt;https://github.com/explosion/spaCy/blob/master/spacy/pipeline/sentencizer.pyx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] spaCy Sentencizer API documentation. &lt;a href="https://spacy.io/api/sentencizer" rel="noopener noreferrer"&gt;https://spacy.io/api/sentencizer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[4] Stack Overflow: spaCy split sentences with abbreviations. &lt;a href="https://stackoverflow.com/questions/53968330/spacy-split-sentences-with-abbreviations" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/53968330/spacy-split-sentences-with-abbreviations&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[5] spaCy Linguistic Features documentation. &lt;a href="https://spacy.io/usage/linguistic-features" rel="noopener noreferrer"&gt;https://spacy.io/usage/linguistic-features&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[6] GitHub issue: Tokenizer exceptions for Sentencizer (#7218). &lt;a href="https://github.com/explosion/spaCy/issues/7218" rel="noopener noreferrer"&gt;https://github.com/explosion/spaCy/issues/7218&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[7] Comment by adrianeboyd on issue #7218. &lt;a href="https://github.com/explosion/spaCy/issues/7218#issuecomment-786685415" rel="noopener noreferrer"&gt;https://github.com/explosion/spaCy/issues/7218#issuecomment-786685415&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[8] yasbd-lib spaCy component API. &lt;a href="https://github.com/speedyk-005/yasbd-lib#spacy-component-api" rel="noopener noreferrer"&gt;https://github.com/speedyk-005/yasbd-lib#spacy-component-api&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nlp</category>
      <category>rulebased</category>
      <category>python</category>
      <category>sentence</category>
    </item>
    <item>
      <title>Four ways to split sentences (without machine learning)</title>
      <dc:creator>Speedyk-005</dc:creator>
      <pubDate>Fri, 24 Jul 2026 03:04:24 +0000</pubDate>
      <link>https://dev.to/speed_k_7e1b449706e59e433/four-ways-to-split-sentences-without-machine-learning-5b28</link>
      <guid>https://dev.to/speed_k_7e1b449706e59e433/four-ways-to-split-sentences-without-machine-learning-5b28</guid>
      <description>&lt;p&gt;Before a machine can summarize text, translate documents, extract entities, or answer questions, it first needs to know where each sentence begins and ends. Get the boundaries wrong, and downstream tasks inherit corrupted input.&lt;/p&gt;

&lt;p&gt;This article explores the design space for rule-based sentence boundary disambiguation (SBD) systems, examining the architectural choices that determine correctness, performance, and maintainability.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem: Punctuation Is Overloaded
&lt;/h2&gt;

&lt;p&gt;The obvious solution seems almost embarrassingly simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(?&amp;lt;=[.!?])(?=\s+[A-Z])&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works... until it doesn't. A period alone does six jobs and only one is "sentence end." Here are just a few situations where naive regex fails:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Why it fails&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Dr. Smith arrived.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Dr. is an abbreviation, not a sentence end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Apple Inc. announced...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inc. isn't the end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;The value is 3.14159.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Decimal point&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;D. H. Lawrence wrote...&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Initials&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Wait... what?&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ellipsis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Version 2.5.1 fixes bugs.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Software version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Visit example.com.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Domain name&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;1. Install Python.&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Numbered list&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Studies estimate that approximately 47% of periods in news text are abbreviations or other non-terminal uses, not sentence boundaries [1]. Multiply this across dozens of languages, each with its own abbreviations, quotation conventions, and punctuation rules, and the scope of the problem becomes clear.&lt;/p&gt;




&lt;h2&gt;
  
  
  Rule-Based vs Machine Learning
&lt;/h2&gt;

&lt;p&gt;SBD systems generally fall into two broad categories.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rule-Based Systems
&lt;/h3&gt;

&lt;p&gt;Rule-based systems encode linguistic knowledge directly. Rather than assuming every period ends a sentence, they examine context such as abbreviations, decimals, initials, URLs, quotation marks, list markers, and surrounding text before making a decision.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fast&lt;/strong&gt;: Minimal algorithmic overhead&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic&lt;/strong&gt;: Same input always yields the same boundaries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explainable&lt;/strong&gt;: Bugs can be traced to specific rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero-shot&lt;/strong&gt;: No training data required&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline-friendly&lt;/strong&gt;: Deploys easily on constrained hardware&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance burden&lt;/strong&gt;: Requires manual curation of abbreviation lists and rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Brittleness&lt;/strong&gt;: Fails on unforeseen formatting or domain shifts&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Machine Learning Systems
&lt;/h3&gt;

&lt;p&gt;ML models treat SBD as a classification problem, learning boundaries from annotated text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Strengths:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Robust&lt;/strong&gt;: Adapts to noisy, unpunctuated, or informal text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contextual&lt;/strong&gt;: Captures multi-token patterns without explicit rules&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;High accuracy&lt;/strong&gt;: Frequently outperforms simple heuristics on benchmarks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Weaknesses:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Black box&lt;/strong&gt;: Debugging requires retraining or fine-tuning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resource-heavy&lt;/strong&gt;: Higher inference latency and memory usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data-dependent&lt;/strong&gt;: Requires annotated training data for each domain or language&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article focuses on rule-based systems, which remain the practical choice for many production pipelines due to their speed, determinism, and explainability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Four Architectural Approaches
&lt;/h2&gt;

&lt;p&gt;Rule-based splitters share a common goal but implement it very differently. Here are the four dominant architectures.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Mask → Split → Demask
&lt;/h3&gt;

&lt;p&gt;Popularized by PySBD [2] and the Ruby Pragmatic Segmenter [3] it was ported from, this approach temporarily replaces ambiguous punctuation with placeholder tokens before splitting.&lt;/p&gt;

&lt;p&gt;For example, the period in &lt;code&gt;Dr.&lt;/code&gt; or &lt;code&gt;example.com&lt;/code&gt; is masked. Once boundaries are identified, the original text is restored.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simplified example
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;mask_split_restore&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Pass 1: mask abbreviation periods
&lt;/span&gt;    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;abbr&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ABBREVIATIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;abbr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;abbr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;∯&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Pass 2: split on remaining periods
&lt;/span&gt;    &lt;span class="n"&gt;sentences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(?&amp;lt;=[.!?])\s+&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="c1"&gt;# Pass 3: restore masked periods
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;∯&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple to implement&lt;/strong&gt; and reason about&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory-heavy&lt;/strong&gt;: Each replacement allocates a new string; large documents can stress garbage collection [4]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multiple passes&lt;/strong&gt;: Requires scanning the text several times&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Span reconstruction&lt;/strong&gt;: Calculating character offsets relative to the original text requires post-processing matching [2]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Placeholder collisions&lt;/strong&gt;: Must use characters that won't appear in the input&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Rule-Based State Machines
&lt;/h3&gt;

&lt;p&gt;Libraries such as spaCy's Sentencizer [5] and syntok [6] process text sequentially, applying contextual rules as they tokenize. Rather than masking punctuation, they maintain internal state and inspect neighboring tokens before declaring a boundary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simplified example
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;state_machine_segment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;!&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;rstrip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ABBREVIATIONS&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sentences&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Integrates naturally&lt;/strong&gt; with tokenization pipelines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoids modifying&lt;/strong&gt; the input text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Architectural complexity&lt;/strong&gt;: Every token passes through evaluation rules; as rule count grows, state transitions become difficult to maintain [7]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokenization dependency&lt;/strong&gt;: Assumes tokenization is already correct&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. One-Pass Pointer-Based Parsing
&lt;/h3&gt;

&lt;p&gt;A hardware-efficient approach that scans raw text once using a moving cursor. As characters are encountered, contextual lookaheads and lookbehinds determine whether a character constitutes a sentence boundary.&lt;/p&gt;

&lt;p&gt;Because the input is never rewritten or rescanned, allocations are minimal. No placeholder management, no restore pass.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simplified example
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;pointer_parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;boundaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;!?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;boundaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;word_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rfind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ABBREVIATIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;boundaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;boundaries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Memory efficient&lt;/strong&gt;: Single pass, no string duplication [8]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardware-friendly&lt;/strong&gt;: Minimal allocations, good cache behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code complexity&lt;/strong&gt;: Adding rules requires threading lookahead logic into a nested loop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error-prone pointer arithmetic&lt;/strong&gt;: Handling nested quotes or ellipses requires care&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Two-Pass Candidate Detection
&lt;/h3&gt;

&lt;p&gt;This architecture (used by yasbd-lib [9]) flips the traditional logic. Instead of shielding text ahead of time, it aggressively identifies all potential boundaries first, then filters out false positives.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pass 1&lt;/strong&gt;: Find every position that could plausibly end a sentence: periods, question marks, exclamation points followed by whitespace, uppercase, or newline. Deliberately over-inclusive.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pass 2&lt;/strong&gt;: Evaluate each candidate against protection rules (abbreviation lists, decimal patterns, URL detection), discarding invalid targets.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Simplified example
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;two_pass_detect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Pass 1: find ALL potential boundary candidates
&lt;/span&gt;    &lt;span class="n"&gt;candidates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finditer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;(?&amp;lt;=[.!?])(?=\s+[A-Z])&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

    &lt;span class="c1"&gt;# Pass 2: filter out invalid points
&lt;/span&gt;    &lt;span class="n"&gt;real_boundaries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;word_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rfind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;word_start&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;lower&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ABBREVIATIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;real_boundaries&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;real_boundaries&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Trade-offs:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Harder to miss a boundary&lt;/strong&gt;: Over-inclusive by design [9]&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separates scanning from validation&lt;/strong&gt;: Cleaner code organization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synchronization risk&lt;/strong&gt;: Pass 1 and Pass 2 must remain aligned&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Requires careful candidate detection&lt;/strong&gt;: Patterns must match what the filter can evaluate&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Design Considerations
&lt;/h2&gt;

&lt;p&gt;Choosing an architecture involves trade-offs across several dimensions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Accuracy
&lt;/h3&gt;

&lt;p&gt;All rule-based systems depend on the quality and completeness of their rule sets. The mask-then-split approach can achieve high accuracy on curated test sets, but its monolithic rule pipeline makes targeted fixes difficult. Two-pass systems isolate validation, making it easier to add or modify rules without affecting candidate detection [9].&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;Mask-then-split performs multiple passes and string allocations, which can become a bottleneck on large documents [4]. Pointer-based and two-pass approaches minimize allocations and are generally faster on modern hardware [8, 9].&lt;/p&gt;

&lt;h3&gt;
  
  
  Maintainability
&lt;/h3&gt;

&lt;p&gt;State machines and pointer-based parsers embed logic in control flow, making rule additions invasive. Mask-then-split centralizes rules but creates interdependencies between transformations. Two-pass systems separate detection from filtering, simplifying rule management [9].&lt;/p&gt;

&lt;h3&gt;
  
  
  Span Tracking
&lt;/h3&gt;

&lt;p&gt;If your pipeline needs character offsets into the original text (for NER training, RAG indexing, or translation alignment), consider how each architecture handles span reconstruction. Mask-then-split requires post-processing matching to map boundaries back to the original string [2]. Pointer-based and two-pass systems track positions directly, avoiding this step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multilingual Support
&lt;/h3&gt;

&lt;p&gt;Rule-based SBD across languages requires per-language abbreviation lists, quotation rules, and punctuation conventions. Some architectures make this easier than others. PySBD supports 22 languages via modular language profiles [2]. yasbd-lib supports 39 languages using a declarative rule system that isolates language-specific data [9]. Two-pass systems with external language packs can add new languages without modifying core code [9].&lt;/p&gt;




&lt;h2&gt;
  
  
  Recommendations
&lt;/h2&gt;

&lt;p&gt;There is no single "best" architecture—each has trade-offs suited to different constraints.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Constraint&lt;/th&gt;
&lt;th&gt;Recommended Architecture&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple implementation, modest text sizes&lt;/td&gt;
&lt;td&gt;Mask → Split → Demask&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration with existing tokenizers&lt;/td&gt;
&lt;td&gt;State machine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minimal memory usage, large documents&lt;/td&gt;
&lt;td&gt;One-pass pointer-based&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ease of rule maintenance and extension&lt;/td&gt;
&lt;td&gt;Two-pass candidate detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accurate character span tracking&lt;/td&gt;
&lt;td&gt;Pointer-based or two-pass&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple languages&lt;/td&gt;
&lt;td&gt;Modular language profiles (any architecture)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For new projects that require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High accuracy&lt;/strong&gt; on diverse text&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easy rule maintenance&lt;/strong&gt; without breaking the pipeline&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Native character span tracking&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multilingual support&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;...a two-pass or pointer-based architecture with modular language profiles is often the most sustainable choice.&lt;/p&gt;

&lt;p&gt;For existing pipelines that already use a mask-then-split library, the question is whether the maintenance burden and performance characteristics are acceptable for your use case.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;[1] E. Stamatatos, N. Fakotakis, and G. Kokkinakis. "Automatic extraction of rules for sentence boundary disambiguation." &lt;em&gt;Proceedings of the Workshop on Machine Learning in Human Language Technology&lt;/em&gt;, University of Patras, pp. 88-92.&lt;/p&gt;

&lt;p&gt;[2] N. Sadvilkar and M. Neumann. "PySBD: Pragmatic Sentence Boundary Disambiguation." &lt;em&gt;arXiv:2010.09657&lt;/em&gt;, 2020. &lt;a href="https://arxiv.org/abs/2010.09657" rel="noopener noreferrer"&gt;https://arxiv.org/abs/2010.09657&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[3] Pragmatic Segmenter (Ruby). &lt;a href="https://github.com/diasks2/pragmatic_segmenter" rel="noopener noreferrer"&gt;https://github.com/diasks2/pragmatic_segmenter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[4] A. M. Kuchling. "Python Performance Tips." &lt;em&gt;Python Documentation&lt;/em&gt;. &lt;a href="https://docs.python.org/3/howto/performance.html" rel="noopener noreferrer"&gt;https://docs.python.org/3/howto/performance.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[5] spaCy Sentencizer. &lt;a href="https://spacy.io/api/sentencizer" rel="noopener noreferrer"&gt;https://spacy.io/api/sentencizer&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[6] F. Leitner. "syntok: Text tokenization and sentence segmentation." &lt;a href="https://github.com/fnl/syntok" rel="noopener noreferrer"&gt;https://github.com/fnl/syntok&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;[7] M. Fowler. &lt;em&gt;Refactoring: Improving the Design of Existing Code&lt;/em&gt;. Addison-Wesley, 1999.&lt;/p&gt;

&lt;p&gt;[8] J. Dean and S. Ghemawat. "MapReduce: Simplified Data Processing on Large Clusters." &lt;em&gt;OSDI&lt;/em&gt; 2004.&lt;/p&gt;

&lt;p&gt;[9] yasbd-lib Documentation. &lt;a href="https://github.com/speedyk-005/yasbd-lib/" rel="noopener noreferrer"&gt;https://github.com/speedyk-005/yasbd-lib/&lt;/a&gt;&lt;/p&gt;

</description>
      <category>nlp</category>
      <category>architecture</category>
      <category>regex</category>
      <category>rulebased</category>
    </item>
    <item>
      <title>Yasbd-lib vs. PySBD: Higher Accuracy, More Languages, Faster Speed</title>
      <dc:creator>Speedyk-005</dc:creator>
      <pubDate>Sun, 19 Jul 2026 19:22:59 +0000</pubDate>
      <link>https://dev.to/speed_k_7e1b449706e59e433/yasbd-lib-vs-pysbd-two-philosophies-of-sentence-boundary-detection-i88</link>
      <guid>https://dev.to/speed_k_7e1b449706e59e433/yasbd-lib-vs-pysbd-two-philosophies-of-sentence-boundary-detection-i88</guid>
      <description>&lt;p&gt;Sentence boundary detection sounds boring. Split on &lt;code&gt;.&lt;/code&gt; &lt;code&gt;?&lt;/code&gt; &lt;code&gt;!&lt;/code&gt;, done, right? Anyone who has tried knows otherwise. Abbreviations, decimals, URLs, nested quotes, ellipsis, legal citations, biomedical jargon—each one turns "split text" into a language-specific puzzle.&lt;/p&gt;

&lt;p&gt;Two Python libraries tackle this problem with different philosophies. &lt;a href="https://github.com/nipunsadvilkar/pySBD/" rel="noopener noreferrer"&gt;pysbd&lt;/a&gt; has been the go-to since 2020 with 23 languages, ported from Ruby's pragmatic segmenter [1]. &lt;a href="https://github.com/speedyk-005/yasbd-lib/" rel="noopener noreferrer"&gt;yasbd-lib&lt;/a&gt; is newer, covers 39 languages, and takes a different architectural approach.&lt;/p&gt;

&lt;p&gt;This is the difference between protecting boundaries and finding them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture: Mutation vs. Pointers
&lt;/h2&gt;

&lt;p&gt;To understand how these engines behave on large datasets, we have to look at how they treat input strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  PySBD: The Transformation Pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;pysbd &lt;span class="nt"&gt;-U&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PySBD operates as a multi-stage transformation pipeline [1]. It treats text as a mutable object that must be modified before it can be split [1]. To prevent punctuation within abbreviations, numbers, or URLs from triggering false splits, PySBD applies regular expressions to replace characters with placeholder tokens [1].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart TD
    A[Raw Input Text] --&amp;gt; B["Replace . with {} / Mask URLs"]
    B --&amp;gt; C[Run Rule Engine]
    C --&amp;gt; D[Split on Splitting Marks]
    D --&amp;gt; E[Reverse Replacement / Restore Text]
    E --&amp;gt; F[Extract Text Segments]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The structural consequence: &lt;strong&gt;the original string layout is transformed during processing.&lt;/strong&gt; Because the text is modified mid-flight, calculating exact character offsets (spans) relative to the original uncleaned text requires a post-processing reconstruction step [1]. If you enable text cleaning (&lt;code&gt;clean=True&lt;/code&gt;), PySBD raises an error when requesting character spans because it cannot guarantee coordinate matching after modification [1].&lt;/p&gt;

&lt;h3&gt;
  
  
  yasbd-lib: The Query Planning Approach
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;yasbd-lib &lt;span class="nt"&gt;-U&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;yasbd-lib&lt;/code&gt; treats text as immutable [2]. It does not modify the raw string [2]. Instead, its architecture resembles a database query planner—generating candidate coordinate arrays and using language-specific filters to narrow down boundary slices [2].&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A[Raw Input String] --&amp;gt; B[Pass 1: Aggressive Candidate Identification]
    B --&amp;gt; C[Pass 2: Modular Filter Elimination]
    C --&amp;gt; D[Project Slices / Return Index Pointers]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By evolving integer pointers rather than altering text strings, &lt;code&gt;yasbd-lib&lt;/code&gt; maintains context of the source layout throughout processing [2]. Token spans are tracked as a first-class structural signal during parsing rather than reconstructed afterward [2].&lt;/p&gt;




&lt;h2&gt;
  
  
  Deep-Dive Feature Breakdown
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Spans and Character Offsets
&lt;/h3&gt;

&lt;p&gt;Because PySBD does not natively track indices during its transformation phase, calculating character offsets requires a post-processing step that searches the original document to locate each sentence [1].&lt;/p&gt;

&lt;h4&gt;
  
  
  The PySBD Reconstruction Step
&lt;/h4&gt;

&lt;p&gt;PySBD reconstructs spans by scanning the original text for each sentence (&lt;a href="https://github.com/nipunsadvilkar/pySBD/blob/5905f13/pysbd/segmenter.py#L59-L77" rel="noopener noreferrer"&gt;source&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;sentences_with_char_spans&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;sent_spans&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="n"&gt;prior_end_char_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;sent&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;finditer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;{0}\s*&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sent&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;original_text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;match_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;group&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="n"&gt;match_start_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;match_end_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;span&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;match_end_idx&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;prior_end_char_idx&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;sent_spans&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nc"&gt;TextSpan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;match_str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;match_start_idx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;match_end_idx&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;prior_end_char_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;match_end_idx&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sent_spans&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Trade-off:&lt;/strong&gt; This reconstruction performs repeated searches over the original document. In pathological cases—such as documents with many repeated sentences—this can approach quadratic behavior. For typical use cases, the overhead is manageable, but it does add runtime cost on longer texts.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  The yasbd-lib Approach
&lt;/h4&gt;

&lt;p&gt;In &lt;code&gt;yasbd-lib&lt;/code&gt;, spans are produced natively during boundary detection [2]. It emits boundary allocations dynamically, avoiding lookback overhead [2]. The library also provides an adapter layer for migrating from PySBD [2]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;yasbd.utils.pysbd_adapter&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Segmenter&lt;/span&gt;

&lt;span class="n"&gt;seg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Segmenter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ja&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;田中さんは「準備は完了しました」そう言って部屋を出た。U.S.A.の経済政策 is complex.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# ['田中さんは「準備は完了しました」そう言って部屋を出た。', 'U.S.A.の経済政策 is complex.']
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Memory and Streaming
&lt;/h3&gt;

&lt;p&gt;PySBD processes text as complete string buffers [1]. &lt;code&gt;yasbd-lib&lt;/code&gt; provides abstractions for memory-constrained environments through lazy evaluation via &lt;code&gt;ParagraphStream&lt;/code&gt; and &lt;code&gt;StreamCleaner&lt;/code&gt; [2]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;yasbd.utils.cleaner&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StreamCleaner&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;yasbd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BoundaryDetector&lt;/span&gt;

&lt;span class="n"&gt;cleaner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StreamCleaner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Hello  world.   This is  messy.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;detector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BoundaryDetector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;sentences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;detector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cleaner&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sentences&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# ['Hello world.', 'This is messy.']
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since v0.14.0 the default cleaning pipeline also normalizes &lt;code&gt;\r\n&lt;/code&gt; and &lt;code&gt;\r&lt;/code&gt; line endings to &lt;code&gt;\n&lt;/code&gt; and rejoins hyphenated words split across line breaks (&lt;code&gt;work-\ning&lt;/code&gt; becomes &lt;code&gt;working&lt;/code&gt;) while keeping legitimate hyphenated compounds intact [2].&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Resource Management
&lt;/h3&gt;

&lt;p&gt;Under the hood of the &lt;code&gt;BoundaryDetector&lt;/code&gt; pipeline, &lt;code&gt;yasbd-lib&lt;/code&gt; manages execution rules using a 5-entry LRU cache (&lt;code&gt;_MAX_CACHED_RULES = 5&lt;/code&gt;) [2]. When using automatic language identification (&lt;code&gt;lang="auto"&lt;/code&gt;), if confidence drops below the threshold (&lt;code&gt;_MIN_CONFIDENCE = 0.8&lt;/code&gt;), the module logs an informational message rather than masking the failure [2]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# From boundary_detector.py
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;classify_language&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;snippet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;confidence&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;_MIN_CONFIDENCE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="nf"&gt;log_info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verbose&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Low confidence ({:.2f}) for detected lang {!r} in auto mode&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;confidence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additionally, &lt;code&gt;yasbd-lib&lt;/code&gt; supports preserving token boundaries inside parentheses or brackets via &lt;code&gt;preserve_quote_and_paren=True&lt;/code&gt; [2].&lt;/p&gt;




&lt;h2&gt;
  
  
  Maintenance Status: A Critical Consideration
&lt;/h2&gt;

&lt;p&gt;The architectural differences matter, but there's another factor: &lt;strong&gt;PySBD is effectively unmaintained.&lt;/strong&gt; As of December 2025, an open issue (#135) [9] notes that the repository has seen no recent updates, with multiple open PRs from contributors and a maintainer who has seemingly abandoned the project. The issue author explicitly requested archiving the project to signal to downstream users that they should no longer incorporate it [9].&lt;/p&gt;

&lt;p&gt;The maintenance situation has real consequences. Consider these unresolved issues:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Issue #79] [10] - Infinite Loop (October 2020):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;segmenter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pysbd&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Segmenter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;..[111 111 111 111 111 111 111 111 111 111]&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;segmenter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;segment&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Hangs indefinitely
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The problem is catastrophic backtracking in &lt;code&gt;NUMBERED_REFERENCE_REGEX&lt;/code&gt;. The maintainer acknowledged it in February 2021, saying "Need to dug into details" [10]. Over four years later, it remains unresolved.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;[Issue #92] [11] - Catastrophic Backtracking in HTMLTagRule (February 2021):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;HTMLTagRule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Rule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;\/?\w+((\s+\w+(\s*=\s*(?:\".*?\"|&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;.*?&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;|[\^&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;\"&amp;gt;\s]+))?)+\s*|\s*)\/?&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When processing unfinished HTML attributes, this regex can cause the segmenter to hang indefinitely [11]. A simplified fix was proposed in the same issue, but it remains unreviewed and unmerged.&lt;/p&gt;

&lt;p&gt;Both issues stem from the same root cause: &lt;strong&gt;regex patterns with nested quantifiers in the transformation pipeline&lt;/strong&gt; [10, 11]. The project has no active maintainer to review or merge fixes [9].&lt;/p&gt;

&lt;p&gt;&lt;code&gt;yasbd-lib&lt;/code&gt; was built in response to this situation, offering a drop-in adapter for PySBD to fix edge cases without heavy refactoring [2, 9].&lt;/p&gt;




&lt;h2&gt;
  
  
  Benchmark Comparisons
&lt;/h2&gt;

&lt;p&gt;The architectural differences influence accuracy and performance.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;PySBD&lt;/th&gt;
&lt;th&gt;yasbd-lib&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python Support&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;3.7–3.11 [1]&lt;/td&gt;
&lt;td&gt;3.10–3.14 [2]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Maintenance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Unmaintained (as of 2025) [9]&lt;/td&gt;
&lt;td&gt;Actively maintained [2]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Known Issues&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Infinite loop on numbered references [10]; catastrophic backtracking in HTML cleaner [11]&lt;/td&gt;
&lt;td&gt;No known catastrophic backtracking issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Approach&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Monolithic transformation pipeline [1]&lt;/td&gt;
&lt;td&gt;Modular immutable pipeline [2]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;State Handling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;String mutation with placeholder tokens [1]&lt;/td&gt;
&lt;td&gt;Pointer-based operations [2]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Language Profiles&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;23 Languages [1]&lt;/td&gt;
&lt;td&gt;39 Languages [2]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;English Golden Score&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;77 / 92 (83.7%) [2]&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;91 / 92 (98.9%)&lt;/strong&gt; [2]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Framework Adapters&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Native API [1]&lt;/td&gt;
&lt;td&gt;spaCy v3+ integration [2]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Benchmark Note:&lt;/strong&gt; The English Golden Score is measured on the project's expanded golden corpus of 92 evaluation cases [2]. The original PySBD corpus contained 48 cases; the expanded set removes ambiguous examples and adds coverage for abbreviation chains, contiguous terminators, and other edge cases. Full methodology and test cases are available in the &lt;a href="https://github.com/speedyk-005/yasbd-lib/tree/main/benchmarks" rel="noopener noreferrer"&gt;benchmarks directory&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fspeedyk-005%2Fyasbd-lib%2Fmain%2Fbenchmarks%2Fbench.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fspeedyk-005%2Fyasbd-lib%2Fmain%2Fbenchmarks%2Fbench.png" alt="SBD Benchmark Performance" width="799" height="498"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A multi-library performance comparison across increasing text sizes. yasbd-lib consistently outperforms alternatives at every scale.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Edge Case Behavior
&lt;/h3&gt;

&lt;p&gt;Consider how both engines handle challenging inputs:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input with numbered references (Issue #79):&lt;/strong&gt; &lt;code&gt;"..[111 111 111 111 111 111 111 111 111 111]"&lt;/code&gt; [10]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PySBD:&lt;/strong&gt; Can enter an infinite loop due to catastrophic backtracking in &lt;code&gt;NUMBERED_REFERENCE_REGEX&lt;/code&gt;. This was reported in October 2020 and remains unresolved [10].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;yasbd-lib:&lt;/strong&gt; The two-pass boundary detection approach avoids complex regex substitutions, preventing this class of issue [2].&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Input with unfinished HTML (Issue #92):&lt;/strong&gt; &lt;code&gt;"&amp;lt;iframe width="100%" ... src="url Lorem ipsum..."&lt;/code&gt; [11]&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PySBD:&lt;/strong&gt; The HTML cleaning regex can cause catastrophic backtracking, hanging the segmenter indefinitely [11]. Reported in February 2021, still unresolved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;yasbd-lib:&lt;/strong&gt; Uses a &lt;code&gt;StreamCleaner&lt;/code&gt; with configurable cleaning steps, including optional HTML unwrapping that avoids nested quantifiers [2].&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Extensibility: Configuration Approaches
&lt;/h2&gt;

&lt;p&gt;What happens when you need to handle custom abbreviations like &lt;code&gt;"Com."&lt;/code&gt; or &lt;code&gt;"Adm."&lt;/code&gt;?&lt;/p&gt;
&lt;h3&gt;
  
  
  PySBD: Internal Rule Modification
&lt;/h3&gt;

&lt;p&gt;Because PySBD's rules operate on a shared transformation timeline, they are interdependent [1]. Adding exceptions requires modifying the internal mutation flow [1].&lt;/p&gt;

&lt;p&gt;As documented in [Issue #108] [3]:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Unfortunately, there is no specific documentation about modifying rules as there are so many and each rule is associated with some form of transformation... All those operations need to be performed in that sequence as they are interrelated... Best way is to use python debugger and see how your input text goes through different transformations."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Adding rules without understanding the full pipeline can break downstream regex patterns [1]. With the project unmaintained, there is no clear path for getting such fixes merged upstream [9].&lt;/p&gt;
&lt;h3&gt;
  
  
  yasbd-lib: Declarative Language Profiles
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;yasbd-lib&lt;/code&gt; decouples matching mechanics from language-specific data [2]. It exposes structured hooks for customization [2]:&lt;/p&gt;

&lt;p&gt;The base &lt;code&gt;Rules&lt;/code&gt; class defines sets for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TITLE_ABBRVS&lt;/strong&gt;: Honorifics that should not split sentences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REFERENCE_ABBRVS&lt;/strong&gt;: Citation abbreviations (&lt;code&gt;fig&lt;/code&gt;, &lt;code&gt;pág&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;INLINE_ONLY_ABBRVS&lt;/strong&gt;: Abbreviations that don't end sentences (&lt;code&gt;blvd&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DATE_ABBRVS&lt;/strong&gt;: Month and weekday abbreviations&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DOTTED_GEOPOL_ABBRVS&lt;/strong&gt;: Geo abbreviations like &lt;code&gt;U.S.&lt;/code&gt;, &lt;code&gt;E.U.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TERMINATORS&lt;/strong&gt;: Extra sentence-ending punctuation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;COMMON_SENT_STARTERS&lt;/strong&gt;: Boundary hints for languages without spaces&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST_QUOTATIVE_PARTICLES&lt;/strong&gt; and &lt;strong&gt;REPORTING_WORDS&lt;/strong&gt;: For dialogue attribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To add a new language, you create a file like &lt;code&gt;fr.py&lt;/code&gt;, subclass &lt;code&gt;Rules&lt;/code&gt; as &lt;code&gt;FrRules&lt;/code&gt;, and override only the sets your language needs. The &lt;a href="https://github.com/speedyk-005/yasbd-lib/blob/main/src/yasbd/rules/_template.py" rel="noopener noreferrer"&gt;language template&lt;/a&gt; [6] provides the structure.&lt;/p&gt;
&lt;h3&gt;
  
  
  Post-Processing Hook
&lt;/h3&gt;

&lt;p&gt;Since v0.14.0, &lt;code&gt;BoundaryDetector&lt;/code&gt; accepts a &lt;code&gt;hook&lt;/code&gt; callback that runs per paragraph after the language rules apply [2]. It receives a dict with &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;lang&lt;/code&gt;, &lt;code&gt;boundaries&lt;/code&gt; and &lt;code&gt;paragraph_index&lt;/code&gt; and can add or remove sentence boundaries in place. This replaces monkey-patching rule internals for custom boundary logic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;yasbd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BoundaryDetector&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;add_extra_boundaries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ctx&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# ctx: {text, lang, boundaries, paragraph_index}
&lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;  &lt;span class="c1"&gt;# mutate ctx["boundaries"] in place
&lt;/span&gt;
&lt;span class="n"&gt;detector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BoundaryDetector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;zh&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;hook&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;add_extra_boundaries&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PySBD offers no equivalent: custom boundary logic there means modifying the internal mutation flow [3].&lt;/p&gt;

&lt;h3&gt;
  
  
  External Language Packs
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;yasbd-lib&lt;/code&gt; supports loading custom language modules at runtime via &lt;code&gt;register_lang_packs()&lt;/code&gt; [2]:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;yasbd.rules&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;register_lang_packs&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;yasbd&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BoundaryDetector&lt;/span&gt;

&lt;span class="nf"&gt;register_lang_packs&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clinical_yasbd_pack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;detector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BoundaryDetector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clinical&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Changes to a language-specific profile do not affect the core engine's boundary detection [2].&lt;/p&gt;

&lt;h4&gt;
  
  
  Language Profile Policy
&lt;/h4&gt;

&lt;p&gt;As documented in [Issue #198] [5], &lt;code&gt;yasbd-lib&lt;/code&gt; has frozen its built-in language set at 39 profiles for the v1.x series to maintain API stability. Additional languages must be loaded externally via &lt;code&gt;register_lang_packs()&lt;/code&gt; using community-maintained packages like &lt;code&gt;yasbd-union&lt;/code&gt; or &lt;code&gt;yasbd-auxlang&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Which Library Should You Choose?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;graph TD
    A[Which SBD to choose?] --&amp;gt; B{Using legacy spaCy v2?}
    B -- Yes --&amp;gt; C[Consider PySBD with caution]
    B -- No --&amp;gt; D{Need active maintenance?}
    D -- Yes --&amp;gt; E[Use yasbd-lib]
    D -- No --&amp;gt; F[Use yasbd-lib for accuracy gains]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Consider PySBD only if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Absolute legacy lock-in:&lt;/strong&gt; You are maintaining an existing pipeline tied to &lt;code&gt;spaCy v2&lt;/code&gt; or older deployments that cannot be migrated. Be aware that the project is unmaintained and has known unresolved issues, including infinite loops with numbered references and catastrophic backtracking with certain HTML inputs [9, 10, 11].&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use yasbd-lib if:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Active maintenance:&lt;/strong&gt; The project is actively maintained with a clear contribution path [2].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance at scale:&lt;/strong&gt; Benchmark results on the &lt;em&gt;Sherlock Holmes&lt;/em&gt; text (594k characters) show yasbd completing in approximately 1.6 seconds compared to 13.3 seconds for PySBD on the same hardware [2]. (These results are from the project's benchmark suite; your mileage may vary based on hardware and Python version.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Character span accuracy:&lt;/strong&gt; Native span tracking may be preferable for downstream tasks like NER training or RAG indexing [2].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-standard inputs:&lt;/strong&gt; The modular design handles raw markdown, chat logs, and multilingual text [2].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custom language rules:&lt;/strong&gt; The declarative profile system simplifies adding new languages or domain-specific abbreviations [2].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration path:&lt;/strong&gt; The included PySBD adapter allows incremental migration without rewriting your entire pipeline [2].&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No catastrophic backtracking:&lt;/strong&gt; The pointer-based architecture avoids the regex issues that plague PySBD's transformation pipeline [2, 10, 11].&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[1] &lt;a href="https://github.com/nipunsadvilkar/pySBD" rel="noopener noreferrer"&gt;PySBD GitHub Repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[2] &lt;a href="https://github.com/speedyk-005/yasbd-lib/" rel="noopener noreferrer"&gt;yasbd-lib GitHub Repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[3] &lt;a href="https://github.com/nipunsadvilkar/pySBD/issues/108" rel="noopener noreferrer"&gt;PySBD Issue #108: Examples of modifying sentence segmentation rules&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[4] &lt;a href="https://github.com/speedyk-005/yasbd-lib/issues/20" rel="noopener noreferrer"&gt;yasbd-lib Issue #20: Help Add More Languages to Yasbd&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[5] &lt;a href="https://github.com/speedyk-005/yasbd-lib/issues/198" rel="noopener noreferrer"&gt;yasbd-lib Issue #198: Core Languages Locked at 39&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[6] &lt;a href="https://github.com/speedyk-005/yasbd-lib/blob/main/src/yasbd/rules/_template.py" rel="noopener noreferrer"&gt;yasbd-lib Language Template&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[7] &lt;a href="https://github.com/speedyk-005/yasbd-lib/tree/main/benchmarks" rel="noopener noreferrer"&gt;yasbd-lib Benchmarks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[8] &lt;a href="https://arxiv.org/abs/2010.09657" rel="noopener noreferrer"&gt;PySBD Paper: arXiv:2010.09657&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[9] &lt;a href="https://github.com/nipunsadvilkar/pySBD/issues/135" rel="noopener noreferrer"&gt;PySBD Issue #135: Archive Project&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[10] &lt;a href="https://github.com/nipunsadvilkar/pySBD/issues/79" rel="noopener noreferrer"&gt;PySBD Issue #79: Infinite loop?&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;[11] &lt;a href="https://github.com/nipunsadvilkar/pySBD/issues/92" rel="noopener noreferrer"&gt;PySBD Issue #92: Catastrophic backtracking in HTMLTagRule&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nlp</category>
      <category>python</category>
      <category>sentencesplitter</category>
      <category>rulebased</category>
    </item>
    <item>
      <title>Introducing chunklet-py v2.2.0+: One Library to Split Them All - Sentence, Code, Docs</title>
      <dc:creator>Speedyk-005</dc:creator>
      <pubDate>Mon, 23 Feb 2026 03:10:16 +0000</pubDate>
      <link>https://dev.to/speed_k_7e1b449706e59e433/-introducing-chunklet-py-dj8</link>
      <guid>https://dev.to/speed_k_7e1b449706e59e433/-introducing-chunklet-py-dj8</guid>
      <description>&lt;p&gt;The Smart Text Chunking Library You Didn't Know You Needed&lt;/p&gt;

&lt;p&gt;Ever tried splitting text for your RAG pipeline and ended up with chunks that cut sentences in half? Or worse — chunks that lose all context between them?&lt;/p&gt;

&lt;p&gt;Yeah, I've been there too. That's exactly why I built &lt;a href="https://github.com/speedyk-005/chunklet-py" rel="noopener noreferrer"&gt;chunklet-py&lt;/a&gt; — a Python library that actually understands text structure.&lt;/p&gt;

&lt;p&gt;This post hits only the highlights and doesn't cover everything — visit the &lt;a href="https://speedyk-005.github.io/chunklet-py/latest/" rel="noopener noreferrer"&gt;full documentation&lt;/a&gt; for everything else, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Custom sentence splitters for specialized languages&lt;/li&gt;
&lt;li&gt;Custom document processors for unusual file formats&lt;/li&gt;
&lt;li&gt;Custom tokenizers to match your LLM&lt;/li&gt;
&lt;li&gt;The rich metadata you can get.&lt;/li&gt;
&lt;li&gt;CLI flags for batch processing, parallel jobs, error handling, timeouts&lt;/li&gt;
&lt;li&gt;Additional args like &lt;code&gt;n_jobs&lt;/code&gt;, &lt;code&gt;lang&lt;/code&gt;, &lt;code&gt;show_progress&lt;/code&gt;, ...&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠ &lt;strong&gt;Quick heads up!&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This tutorial requires &lt;code&gt;chunklet-py v2.2.0+&lt;/code&gt; and uses APIs not available in earlier versions. &lt;/p&gt;

&lt;p&gt;Upgrade to the latest version and see the &lt;a href="https://speedyk-005.github.io/chunklet-py/latest/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; or &lt;a href="https://speedyk-005.github.io/chunklet-py/latest/whats-new/" rel="noopener noreferrer"&gt;What’s New&lt;/a&gt; for details.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Problem with Dumb Splitting
&lt;/h2&gt;

&lt;p&gt;Here's what usually happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# The naive approach
&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works... until it doesn't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sentences cut mid-way&lt;/li&gt;
&lt;li&gt;No context between chunks&lt;/li&gt;
&lt;li&gt;Broken code if you're chunking source files&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solution: chunklet-py
&lt;/h2&gt;

&lt;p&gt;A smart text and code chunking library that respects natural boundaries.&lt;/p&gt;

&lt;h3&gt;
  
  
  Features
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;50+ languages supported&lt;/strong&gt; — Auto-detects language and applies the right splitting rules. No more treating German the same as English.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multiple constraint types&lt;/strong&gt; — Mix and match:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;max_sentences&lt;/code&gt; — group by sentences&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_tokens&lt;/code&gt; — respect LLM context limits
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_section_breaks&lt;/code&gt; — keep Markdown headers together (headings &lt;code&gt;##&lt;/code&gt;, horizontal rules &lt;code&gt;---&lt;/code&gt;, &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; tags)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_lines&lt;/code&gt; — for code chunking&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;max_functions&lt;/code&gt; — keep functions together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Multiple file formats&lt;/strong&gt; — PDF,PPTX, EML, DOCX, EPUB, HTML, Markdown, LaTeX, ODT, CSV, Excel, plain text — one library handles them all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rich metadata&lt;/strong&gt; — Every chunk comes with source references, character spans, and structural info.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Composable constraints&lt;/strong&gt; — Mix and match limits to get exactly the chunks you need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pluggable architecture&lt;/strong&gt; — Swap in custom tokenizers, sentence splitters, or document processors.&lt;/p&gt;

&lt;h3&gt;
  
  
  What's New in v2.2.0
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;API Unification&lt;/strong&gt; — Methods renamed to &lt;code&gt;chunk_text&lt;/code&gt;, &lt;code&gt;chunk_file&lt;/code&gt;, &lt;code&gt;chunk_texts&lt;/code&gt;, &lt;code&gt;chunk_files&lt;/code&gt; for consistency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Visualizer redesign&lt;/strong&gt; — Fullscreen mode, 3-row layout, smoother hovers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More code languages&lt;/strong&gt; — ColdFusion, VB.NET, PHP 8 attributes, Pascal support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ruff&lt;/strong&gt; — Switched to Ruff for faster linting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check the &lt;a href="https://speedyk-005.github.io/chunklet-py/latest/whats-new/" rel="noopener noreferrer"&gt;What's New&lt;/a&gt; page for full details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;chunklet-py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For document support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;chunklet-py[structured-document]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;chunklet-py[code]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For visualization:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;chunklet-py[visualization]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Code Examples
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Core Imports
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chunklet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;DocumentChunker&lt;/span&gt;   &lt;span class="c1"&gt;# For PDFs, DOCX, and general text
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chunklet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CodeChunker&lt;/span&gt;       &lt;span class="c1"&gt;# For source code
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chunklet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceSplitter&lt;/span&gt;  &lt;span class="c1"&gt;# For just sentences
&lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chunklet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;visualizer&lt;/span&gt;        &lt;span class="c1"&gt;# Web-based visualizer
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  DocumentChunker API
&lt;/h3&gt;

&lt;p&gt;Four methods cover most use cases:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Return Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chunk_text(text)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;str&lt;/td&gt;
&lt;td&gt;List[Chunk]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chunk_file(path)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Path or str&lt;/td&gt;
&lt;td&gt;List[Chunk]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chunk_texts(list)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List[str]&lt;/td&gt;
&lt;td&gt;Generator[Chunk]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chunk_files(list)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;List[Path]&lt;/td&gt;
&lt;td&gt;Generator[Chunk]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  DocumentChunker Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chunker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DocumentChunker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;# Feel free to mix and match these
&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_sentences&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# Stop after X sentences
&lt;/span&gt;    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# Don't blow up the LLM context
&lt;/span&gt;    &lt;span class="n"&gt;max_section_breaks&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Respect the Markdown headers
&lt;/span&gt;    &lt;span class="n"&gt;overlap_percent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;# Give it some "memory" of the last chunk
&lt;/span&gt;    &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;               &lt;span class="c1"&gt;# Skip the first N sentences
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CodeChunker Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chunker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CodeChunker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chunk_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_lines&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# Height limit
&lt;/span&gt;    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;# Width limit
&lt;/span&gt;    &lt;span class="n"&gt;max_functions&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;# One function per chunk
&lt;/span&gt;    &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;# True: Crash on big blocks; False: Slice anyway
&lt;/span&gt;    &lt;span class="n"&gt;include_comments&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# True by default
&lt;/span&gt;    &lt;span class="n"&gt;docstring_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# Options are: all, excluded, summary
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;⚠ &lt;strong&gt;Token Counter Requirement&lt;/strong&gt;&lt;br&gt;
When using the max_tokens constraint, a token_counter function is essential. This function, which you provide, should accept a string and return an integer representing its token count. Failing to provide a token_counter will result in a &lt;a href="https://speedyk-005.github.io/chunklet-py/latest/exceptions-and-warnings/#missingtokencountererror" rel="noopener noreferrer"&gt;MissingTokenCounterError&lt;/a&gt;.&lt;br&gt;
You can also provide the token_counter directly to any chunking method. If provided in both the constructor and the method, the one in the method will be used.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  SentenceSplitter (Just Sentences)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chunklet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SentenceSplitter&lt;/span&gt;

&lt;span class="n"&gt;splitter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SentenceSplitter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;sentences&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;splitter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split_text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# You can also set it to "auto"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handles tricky cases like "Dr." or "U.S.A." without breaking them up.&lt;/p&gt;

&lt;p&gt;50+ languages are explicitly supported through dedicated libraries (pysbd covers 40+, Indic NLP Library covers 11, sentsplit covers 4, and Sentencex covers ~15, with some overlap), plus the Fallback Splitter handles any other language via Unicode rules (&lt;a href="https://speedyk-005.github.io/chunklet-py/latest/supported-languages/" rel="noopener noreferrer"&gt;Supported Languages Documentation&lt;/a&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  Output Object
&lt;/h3&gt;

&lt;p&gt;Chunkers return Chunk objects (Box instances), so you use dot notation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# The actual text/code
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Chunk metadata
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Visualizer (Interactive Web UI)
&lt;/h3&gt;

&lt;p&gt;Launch a web interface to experiment with chunking parameters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chunklet visualize
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or programmatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chunklet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;visualizer&lt;/span&gt;

&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;visualizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Visualizer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;8000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Opens in your browser
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  CLI Examples
&lt;/h2&gt;

&lt;p&gt;Prefer the terminal? chunklet-py ships with a &lt;a href="https://speedyk-005.github.io/chunklet-py/latest/getting-started/cli/" rel="noopener noreferrer"&gt;full CLI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are some quick examples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Basic text chunking&lt;/span&gt;
chunklet chunk &lt;span class="s2"&gt;"Your text here."&lt;/span&gt; &lt;span class="nt"&gt;--max-tokens&lt;/span&gt; 500

&lt;span class="c"&gt;# Chunk a file&lt;/span&gt;
chunklet chunk &lt;span class="nt"&gt;--source&lt;/span&gt; document.pdf &lt;span class="nt"&gt;--max-tokens&lt;/span&gt; 500 &lt;span class="nt"&gt;--metadata&lt;/span&gt;

&lt;span class="c"&gt;# Split text into sentences&lt;/span&gt;
chunklet &lt;span class="nb"&gt;split&lt;/span&gt; &lt;span class="s2"&gt;"Your text here."&lt;/span&gt; &lt;span class="nt"&gt;--lang&lt;/span&gt; en

&lt;span class="c"&gt;# Split a file into sentences&lt;/span&gt;
chunklet &lt;span class="nb"&gt;split&lt;/span&gt; &lt;span class="nt"&gt;--source&lt;/span&gt; my_file.txt &lt;span class="nt"&gt;--destination&lt;/span&gt; sentences.txt

&lt;span class="c"&gt;# Start the interactive visualizer&lt;/span&gt;
chunklet visualize

&lt;span class="c"&gt;# Code chunking&lt;/span&gt;
chunklet chunk &lt;span class="nt"&gt;--code&lt;/span&gt; &lt;span class="nt"&gt;--source&lt;/span&gt; my_script.py &lt;span class="nt"&gt;--max-functions&lt;/span&gt; 1

&lt;span class="c"&gt;# Batch processing a directory&lt;/span&gt;
chunklet chunk &lt;span class="nt"&gt;--doc&lt;/span&gt; &lt;span class="nt"&gt;--source&lt;/span&gt; ./my_docs &lt;span class="nt"&gt;--destination&lt;/span&gt; ./chunks &lt;span class="nt"&gt;--n-jobs&lt;/span&gt; 4

&lt;span class="c"&gt;# With error handling&lt;/span&gt;
chunklet chunk &lt;span class="nt"&gt;--doc&lt;/span&gt; &lt;span class="nt"&gt;--source&lt;/span&gt; ./my_docs &lt;span class="nt"&gt;--on-errors&lt;/span&gt; skip
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How It Compares
&lt;/h2&gt;

&lt;p&gt;While there are other chunking libraries available, Chunklet-py stands out for its unique combination of versatility, performance, and ease of use. Here's a quick look at how it compares to some of the alternatives:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Library&lt;/th&gt;
&lt;th&gt;Key Differentiator&lt;/th&gt;
&lt;th&gt;Focus&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;chunklet-py&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;All-in-one, lightweight, multilingual, language-agnostic with specialized algorithms.&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Text, Code, Docs&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/langchain-ai/langchain" rel="noopener noreferrer"&gt;LangChain&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Full LLM framework with basic splitters (e.g., RecursiveCharacterTextSplitter, Markdown, HTML, code splitters). Good for prototyping but basic for complex docs or multilingual needs.&lt;/td&gt;
&lt;td&gt;Full Stack&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/chonkie-inc/chonkie" rel="noopener noreferrer"&gt;Chonkie&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;All-in-one pipeline (chunking + embeddings + vector DB). Uses &lt;code&gt;tree-sitter&lt;/code&gt; for code. Multilingual.&lt;/td&gt;
&lt;td&gt;Pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/isaacus-dev/semchunk" rel="noopener noreferrer"&gt;Semchunk&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Text-only, fast semantic splitting. Built-in tiktoken/HuggingFace support. 85% faster than alternatives.&lt;/td&gt;
&lt;td&gt;Text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/CintraAI/code-chunker" rel="noopener noreferrer"&gt;CintraAI Code Chunker&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Code-specific, uses &lt;code&gt;tree-sitter&lt;/code&gt;. Initially supports Python, JS, CSS only.&lt;/td&gt;
&lt;td&gt;Code&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Chunklet-py is a specialized, drop-in replacement for the chunking step in any RAG pipeline. It handles text, documents, and code without heavy dependencies, while keeping your project lightweight.&lt;/p&gt;

</description>
      <category>rag</category>
      <category>chunk</category>
      <category>llm</category>
      <category>python</category>
    </item>
    <item>
      <title>Stop Breaking Context: Smarter Text Chunking for Python NLP Projects</title>
      <dc:creator>Speedyk-005</dc:creator>
      <pubDate>Wed, 13 Aug 2025 21:59:51 +0000</pubDate>
      <link>https://dev.to/speed_k_7e1b449706e59e433/stop-breaking-context-smarter-text-chunking-for-python-nlp-projects-2n8n</link>
      <guid>https://dev.to/speed_k_7e1b449706e59e433/stop-breaking-context-smarter-text-chunking-for-python-nlp-projects-2n8n</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;Chunklet: Smarter Text Chunking for Python Developers&lt;/strong&gt;
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;⚠ &lt;strong&gt;This post is outdated&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This guide uses &lt;code&gt;chunklet v1.x&lt;/code&gt;, which is no longer maintained. see the Migration Guide: &lt;a href="https://speedyk-005.github.io/chunklet-py/latest/migration/" rel="noopener noreferrer"&gt;https://speedyk-005.github.io/chunklet-py/latest/migration/&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;👉 Use &lt;code&gt;chunklet-py v2.x&lt;/code&gt; instead:&lt;br&gt;&lt;br&gt;
&lt;a href="https://dev.to/speed_k_7e1b449706e59e433/chunklet-py-one-library-to-split-them-all-sentence-code-docs-2eeg"&gt;https://dev.to/speed_k_7e1b449706e59e433/chunklet-py-one-library-to-split-them-all-sentence-code-docs-2eeg&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;🚀 Latest version (v2.2.0+):&lt;br&gt;&lt;br&gt;
&lt;a href="https://dev.to/speed_k_7e1b449706e59e433/-introducing-chunklet-py-dj8"&gt;https://dev.to/speed_k_7e1b449706e59e433/-introducing-chunklet-py-dj8&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why Context Matters in Text Splitting&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When preprocessing documents for NLP tasks, standard splitting methods often:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Break sentences mid-thought (&lt;code&gt;"The patient showed improvement. However," → "However,"&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Ignore linguistic boundaries in non-English texts&lt;/li&gt;
&lt;li&gt;Lose critical context between chunks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Chunklet solves this with structural awareness.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;1. Installation &amp;amp; Basic Usage&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;chunklet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Minimal Example:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;chunklet&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Chunklet&lt;/span&gt;

&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;First sentence. Second sentence. Third sentence.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;chunker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Chunklet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sentence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max_sentences&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Output:
# ["First sentence. Second sentence.", "Third sentence."]
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;This preserves complete sentences while respecting chunk size limits.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;2. Key Features Explained&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Hybrid Chunking Mode&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Combines structural and size-based splitting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hybrid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_sentences&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Structural limit
&lt;/span&gt;    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="c1"&gt;# Size limit
&lt;/span&gt;    &lt;span class="n"&gt;overlap_percent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;  &lt;span class="c1"&gt;# Context preservation
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Why this matters:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevents chunks from becoming too long or too short&lt;/li&gt;
&lt;li&gt;Overlap maintains relationships between sections&lt;/li&gt;
&lt;li&gt;Works equally well on code, markdown, or prose&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Multilingual Support&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Auto-detection (36+ languages)
&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;multilingual_text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Manual override
&lt;/span&gt;&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;japanese_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ja&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;How it works:&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uses &lt;code&gt;py3langid&lt;/code&gt; for fast language detection&lt;/li&gt;
&lt;li&gt;Applies language-specific sentence boundaries&lt;/li&gt;
&lt;li&gt;Falls back to regex for unsupported languages&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;3. Real-World Use Cases&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Preparing Legal Documents&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;legal_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;contract.txt&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;read_text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;chunks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;legal_text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hybrid&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;max_tokens&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;overlap_percent&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;  &lt;span class="c1"&gt;# Critical for clause relationships
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Why it works:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Preserves entire contract clauses&lt;/li&gt;
&lt;li&gt;Maintains references between sections (e.g., "as defined in Section 2.1")&lt;/li&gt;
&lt;li&gt;Handles complex punctuation in legal prose&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Processing Academic Papers&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;chunker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Chunklet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sentence_splitter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;custom_academic_splitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;# Handles citations
&lt;/span&gt;    &lt;span class="n"&gt;token_counter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;scibert_tokenizer&lt;/span&gt;  &lt;span class="c1"&gt;# Domain-specific counting
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Customization options:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plug in any sentence splitter&lt;/li&gt;
&lt;li&gt;Use HuggingFace tokenizers&lt;/li&gt;
&lt;li&gt;Adjust chunking thresholds per document type&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;4. Performance Considerations&lt;/strong&gt;
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# For large datasets:
&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chunker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;batch_chunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;documents&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;n_jobs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;          &lt;span class="c1"&gt;# Parallel processing
&lt;/span&gt;    &lt;span class="n"&gt;chunk_size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;     &lt;span class="c1"&gt;# Documents per batch
&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Optimization tips:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enable &lt;code&gt;use_cache=True&lt;/code&gt; for repeated texts&lt;/li&gt;
&lt;li&gt;Pre-filter very short/long documents&lt;/li&gt;
&lt;li&gt;Monitor memory with &lt;code&gt;memory_profiler&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Ready to try?&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/Speedyk-005/chunklet" rel="noopener noreferrer"&gt;GitHub Repository&lt;/a&gt; | &lt;a href="https://pypi.org/project/chunklet/" rel="noopener noreferrer"&gt;PyPI Package&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>nlp</category>
      <category>opensource</category>
      <category>rag</category>
    </item>
  </channel>
</rss>
