chunklet-py 2.3.1__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/LICENSE +1 -1
- {chunklet_py-2.3.1/src/chunklet_py.egg-info → chunklet_py-2.4.0}/PKG-INFO +21 -13
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/README.md +15 -4
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/pyproject.toml +10 -20
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/base_chunker.py +1 -2
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/cli.py +2 -2
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/code_chunker/_code_structure_extractor.py +21 -10
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/code_chunker/code_chunker.py +77 -38
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/common/deprecation.py +2 -1
- chunklet_py-2.4.0/src/chunklet/common/dotdict.py +176 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/common/path_utils.py +1 -2
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/common/validation.py +12 -11
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/_plain_text_chunker.py +7 -5
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/converters/rst_2_md.py +1 -3
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/converters/table_2_md.py +2 -17
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/document_chunker.py +20 -8
- chunklet_py-2.4.0/src/chunklet/document_chunker/md_table.py +36 -0
- chunklet_py-2.4.0/src/chunklet/document_chunker/processors/eml_processor.py +117 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/processors/epub_processor.py +1 -1
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/processors/odt_processor.py +0 -1
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/processors/pdf_processor.py +1 -3
- chunklet_py-2.4.0/src/chunklet/document_chunker/processors/pptx_processor.py +326 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/span_finder.py +9 -11
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/sentence_splitter/_universal_splitter.py +8 -10
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/sentence_splitter/languages.py +10 -40
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/sentence_splitter/sentence_splitter.py +13 -20
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/sentence_splitter/terminators.py +0 -2
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/visualizer/static/index.html +2 -1
- {chunklet_py-2.3.1 → chunklet_py-2.4.0/src/chunklet_py.egg-info}/PKG-INFO +21 -13
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet_py.egg-info/SOURCES.txt +4 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet_py.egg-info/requires.txt +4 -7
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/tests/test_document_chunking.py +3 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/tests/test_plain_text_chunking.py +5 -3
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/tests/test_sentence_splitting.py +21 -28
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/tests/test_visualization.py +4 -4
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/setup.cfg +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/__init__.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/code_chunker/__init__.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/code_chunker/patterns.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/code_chunker/utils.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/common/__init__.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/common/batch_runner.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/common/logging_utils.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/common/token_utils.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/__init__.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/converters/__init__.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/converters/html_2_md.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/converters/latex_2_md.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/processors/__init__.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/processors/base_processor.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/processors/docx_processor.py +1 -1
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/document_chunker/registry.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/exceptions.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/sentence_splitter/__init__.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/sentence_splitter/registry.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/visualizer/__init__.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/visualizer/static/css/style.css +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/visualizer/static/js/app.js +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/visualizer/static/js/utils.js +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/visualizer/visualizer.py +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet_py.egg-info/dependency_links.txt +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet_py.egg-info/entry_points.txt +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet_py.egg-info/top_level.txt +0 -0
- {chunklet_py-2.3.1 → chunklet_py-2.4.0}/tests/test_code_chunking.py +0 -0
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
21
|
+
SOFTWARE.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chunklet-py
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: High-fidelity context-aware chunking and interactive visualization for RAG. Advanced segmentation for code and documents, because your LLM is only as smart as the fragments you feed it.
|
|
5
5
|
Author-email: speedyk_005 <speedy40115719@gmail.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -9,7 +9,7 @@ Project-URL: Repository, https://github.com/speedyk-005/chunklet-py
|
|
|
9
9
|
Project-URL: Documentation, https://speedyk-005.github.io/chunklet-py/
|
|
10
10
|
Project-URL: Issues, https://github.com/speedyk-005/chunklet-py/issues
|
|
11
11
|
Project-URL: Changelog, https://github.com/speedyk-005/chunklet-py/blob/main/CHANGELOG.md
|
|
12
|
-
Keywords: nlp,chunking,text-splitting,sentence-splitting,llm,rag,ai,multilingual,text processing,natural language processing,data processing,information retrieval,semantic search,document processing,document-chunker,code chunking,code-chunker,
|
|
12
|
+
Keywords: nlp,chunking,chunker,text-chunking,text-splitting,sentence-splitting,llm,rag,ai,multilingual,text processing,natural language processing,data processing,information retrieval,semantic search,document processing,document-chunker,code chunking,code-chunker,programming languages,code structure,chunk-visualization
|
|
13
13
|
Classifier: Development Status :: 5 - Production/Stable
|
|
14
14
|
Classifier: Intended Audience :: Developers
|
|
15
15
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -26,20 +26,17 @@ Requires-Python: >=3.11
|
|
|
26
26
|
Description-Content-Type: text/markdown
|
|
27
27
|
License-File: LICENSE
|
|
28
28
|
Requires-Dist: loguru<1.0,>=0.7.3
|
|
29
|
-
Requires-Dist:
|
|
30
|
-
Requires-Dist: sentsplit<2.0,>=1.0.8
|
|
29
|
+
Requires-Dist: yasbd-lib<2.0,>=0.12.0
|
|
31
30
|
Requires-Dist: sentencex<=0.6.1; sys_platform == "linux" and (platform_machine == "aarch64" or platform_machine == "armv7l")
|
|
32
31
|
Requires-Dist: sentencex<2.0,>=1.0.0; sys_platform != "linux" or (platform_machine != "aarch64" and platform_machine != "armv7l")
|
|
33
32
|
Requires-Dist: indic-nlp-library<1.0,>=0.92
|
|
34
33
|
Requires-Dist: py3langid<1.0,>=0.3.0
|
|
35
34
|
Requires-Dist: mpire<3.0,>=2.10.2
|
|
36
|
-
Requires-Dist: dotdict3<2.0,>=1.1.0
|
|
37
35
|
Requires-Dist: more-itertools<12.0,>=10.6.0
|
|
38
36
|
Requires-Dist: regex>=2025.7.29
|
|
39
37
|
Requires-Dist: pydantic<3.0,>=2.11.0
|
|
40
38
|
Requires-Dist: typer<1.0,>=0.19.0
|
|
41
39
|
Requires-Dist: charset-normalizer<4.0,>=3.4.2
|
|
42
|
-
Requires-Dist: setuptools<81
|
|
43
40
|
Provides-Extra: structured-document
|
|
44
41
|
Requires-Dist: pdfminer.six>=20250324; extra == "structured-document"
|
|
45
42
|
Requires-Dist: python-docx<2.0,>=1.2.0; extra == "structured-document"
|
|
@@ -48,10 +45,11 @@ Requires-Dist: ebooklib<1.0,>=0.19; extra == "structured-document"
|
|
|
48
45
|
Requires-Dist: docutils<1.0,>=0.21.2; extra == "structured-document"
|
|
49
46
|
Requires-Dist: markdownify<2.0,>=1.1.0; extra == "structured-document"
|
|
50
47
|
Requires-Dist: striprtf<1.0,>=0.0.29; extra == "structured-document"
|
|
51
|
-
Requires-Dist:
|
|
48
|
+
Requires-Dist: mailparse<2.0,>=1.0.1; extra == "structured-document"
|
|
49
|
+
Requires-Dist: python-pptx<2.0,>=1.0.0; extra == "structured-document"
|
|
50
|
+
Requires-Dist: pylatexenc==2.11; extra == "structured-document"
|
|
52
51
|
Requires-Dist: odfpy<2.0,>=1.4.1; extra == "structured-document"
|
|
53
52
|
Requires-Dist: openpyxl<4.0,>=3.1.5; extra == "structured-document"
|
|
54
|
-
Requires-Dist: tabulate2<2.0,>=1.10.2; extra == "structured-document"
|
|
55
53
|
Provides-Extra: document
|
|
56
54
|
Requires-Dist: chunklet-py[structured-document]; extra == "document"
|
|
57
55
|
Provides-Extra: code
|
|
@@ -71,7 +69,6 @@ Provides-Extra: dev
|
|
|
71
69
|
Requires-Dist: pytest>=8.3.5; extra == "dev"
|
|
72
70
|
Requires-Dist: pytest-cov>=6.2.1; extra == "dev"
|
|
73
71
|
Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
|
|
74
|
-
Requires-Dist: pytest-timeout>=2.3.1; extra == "dev"
|
|
75
72
|
Requires-Dist: ruff>=0.14.14; extra == "dev"
|
|
76
73
|
Provides-Extra: docs
|
|
77
74
|
Requires-Dist: mike>=2.1.2; extra == "docs"
|
|
@@ -129,7 +126,7 @@ Dumb splitting causes problems:
|
|
|
129
126
|
Smart chunking solves this by:
|
|
130
127
|
|
|
131
128
|
- **Smart limits** — Respects both natural boundaries (sentences, paragraphs, sections) AND configurable limits (tokens, lines, functions)
|
|
132
|
-
- **Language-aware** — Detects language automatically and applies the right rules (
|
|
129
|
+
- **Language-aware** — Detects language automatically and applies the right rules (60+ languages supported)
|
|
133
130
|
- **Context preservation** — Overlap between chunks, rich metadata (source, span, document structure)
|
|
134
131
|
|
|
135
132
|
## 🤔 So What's Chunklet-py Anyway? (And Why Should You Care?)
|
|
@@ -158,7 +155,7 @@ Perfect for prepping data for LLMs, building RAG systems, or powering AI search
|
|
|
158
155
|
| 🪶 **Featherlight Footprint** | Designed to be lightweight and memory-efficient, ensuring optimal performance without unnecessary overhead. |
|
|
159
156
|
| 🗂️ **Rich Metadata for RAG** | Enriches chunks with valuable, context-aware metadata (source, span, document properties, code AST details) crucial for advanced RAG and LLM applications. |
|
|
160
157
|
| 🔧 **Infinitely Customizable** | Offers extensive customization options, from pluggable token counters to custom sentence splitters and processors. |
|
|
161
|
-
| 🌐 **Multilingual Mastery** | Supports over
|
|
158
|
+
| 🌐 **Multilingual Mastery** | Supports over 60 natural languages for text and document chunking with intelligent detection and language-specific algorithms. |
|
|
162
159
|
| 🧑💻 **Code-Aware Intelligence** | Language-agnostic code chunking that understands and preserves the structural integrity of your source code. |
|
|
163
160
|
| 🎯 **Precision Chunking** | Flexible chunking with configurable limits based on sentences, tokens, sections, lines, and functions. |
|
|
164
161
|
| 📄 **Document Format Mastery** | Processes a wide array of document formats including `.pdf`, `.docx`, `.epub`, `.txt`, `.tex`, `.html`, `.hml`, `.md`, `.rst`, `.rtf`, `.odt`, `.csv`, and `.xlsx`. |
|
|
@@ -185,19 +182,21 @@ The simplest way to get started is with pip:
|
|
|
185
182
|
|
|
186
183
|
```bash
|
|
187
184
|
# Install and check it's working
|
|
188
|
-
pip install chunklet-py
|
|
185
|
+
pip install chunklet-py -U
|
|
189
186
|
chunklet --version
|
|
190
187
|
```
|
|
191
188
|
|
|
192
189
|
> [!TIP]
|
|
193
190
|
> <b>Termux (Android)</b>
|
|
194
191
|
>
|
|
195
|
-
> No rust toolchain on Termux (especially python 3.13)?
|
|
192
|
+
> No rust toolchain on Termux (especially python 3.13)? Pydantic-core and cryptography won't build from source.
|
|
193
|
+
> Install pre-built wheels and dependencies:
|
|
196
194
|
>
|
|
197
195
|
> ```bash
|
|
198
196
|
> pip install typing-extensions
|
|
199
197
|
> pip install pydantic-core --index-url https://termux-user-repository.github.io/pypi/
|
|
200
198
|
> pip install "pydantic>=2.12.4,<2.13"
|
|
199
|
+
> pkg install python-cryptography
|
|
201
200
|
> ```
|
|
202
201
|
|
|
203
202
|
That's it! You're all set to start chunking.
|
|
@@ -217,6 +216,8 @@ Want to unlock more Chunklet-py superpowers? Add these optional dependencies bas
|
|
|
217
216
|
* **Visualization:** For the interactive web-based chunk visualizer:
|
|
218
217
|
```bash
|
|
219
218
|
pip install "chunklet-py[visualization]"
|
|
219
|
+
# Or
|
|
220
|
+
pip install "chunklet-py[viz]"
|
|
220
221
|
```
|
|
221
222
|
* **All Extras:** To install all optional dependencies:
|
|
222
223
|
```bash
|
|
@@ -352,6 +353,13 @@ splitter = SentenceSplitter()
|
|
|
352
353
|
sentences = splitter.split_text(text, lang="auto")
|
|
353
354
|
```
|
|
354
355
|
|
|
356
|
+
> [!TIP]
|
|
357
|
+
> **Advanced text splitting**
|
|
358
|
+
>
|
|
359
|
+
> Chunklet-py quietly delegates sentence splitting to [yasbd-lib](https://github.com/speedyk-005/yasbd-lib), our high-accuracy SBD supporting **39 languages**.
|
|
360
|
+
>
|
|
361
|
+
> Curious why we built it? Read **[yasbd-lib vs PySBD: Two Philosophies of Sentence Boundary Detection](https://dev.to/speed_k_7e1b449706e59e433/yasbd-lib-vs-pysbd-two-philosophies-of-sentence-boundary-detection-i88)**.
|
|
362
|
+
|
|
355
363
|
**CLI (Command Line Interface)**
|
|
356
364
|
|
|
357
365
|
If you prefer the terminal to an IDE, the CLI is packed with features. Just ask for help.
|
|
@@ -44,7 +44,7 @@ Dumb splitting causes problems:
|
|
|
44
44
|
Smart chunking solves this by:
|
|
45
45
|
|
|
46
46
|
- **Smart limits** — Respects both natural boundaries (sentences, paragraphs, sections) AND configurable limits (tokens, lines, functions)
|
|
47
|
-
- **Language-aware** — Detects language automatically and applies the right rules (
|
|
47
|
+
- **Language-aware** — Detects language automatically and applies the right rules (60+ languages supported)
|
|
48
48
|
- **Context preservation** — Overlap between chunks, rich metadata (source, span, document structure)
|
|
49
49
|
|
|
50
50
|
## 🤔 So What's Chunklet-py Anyway? (And Why Should You Care?)
|
|
@@ -73,7 +73,7 @@ Perfect for prepping data for LLMs, building RAG systems, or powering AI search
|
|
|
73
73
|
| 🪶 **Featherlight Footprint** | Designed to be lightweight and memory-efficient, ensuring optimal performance without unnecessary overhead. |
|
|
74
74
|
| 🗂️ **Rich Metadata for RAG** | Enriches chunks with valuable, context-aware metadata (source, span, document properties, code AST details) crucial for advanced RAG and LLM applications. |
|
|
75
75
|
| 🔧 **Infinitely Customizable** | Offers extensive customization options, from pluggable token counters to custom sentence splitters and processors. |
|
|
76
|
-
| 🌐 **Multilingual Mastery** | Supports over
|
|
76
|
+
| 🌐 **Multilingual Mastery** | Supports over 60 natural languages for text and document chunking with intelligent detection and language-specific algorithms. |
|
|
77
77
|
| 🧑💻 **Code-Aware Intelligence** | Language-agnostic code chunking that understands and preserves the structural integrity of your source code. |
|
|
78
78
|
| 🎯 **Precision Chunking** | Flexible chunking with configurable limits based on sentences, tokens, sections, lines, and functions. |
|
|
79
79
|
| 📄 **Document Format Mastery** | Processes a wide array of document formats including `.pdf`, `.docx`, `.epub`, `.txt`, `.tex`, `.html`, `.hml`, `.md`, `.rst`, `.rtf`, `.odt`, `.csv`, and `.xlsx`. |
|
|
@@ -100,19 +100,21 @@ The simplest way to get started is with pip:
|
|
|
100
100
|
|
|
101
101
|
```bash
|
|
102
102
|
# Install and check it's working
|
|
103
|
-
pip install chunklet-py
|
|
103
|
+
pip install chunklet-py -U
|
|
104
104
|
chunklet --version
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
> [!TIP]
|
|
108
108
|
> <b>Termux (Android)</b>
|
|
109
109
|
>
|
|
110
|
-
> No rust toolchain on Termux (especially python 3.13)?
|
|
110
|
+
> No rust toolchain on Termux (especially python 3.13)? Pydantic-core and cryptography won't build from source.
|
|
111
|
+
> Install pre-built wheels and dependencies:
|
|
111
112
|
>
|
|
112
113
|
> ```bash
|
|
113
114
|
> pip install typing-extensions
|
|
114
115
|
> pip install pydantic-core --index-url https://termux-user-repository.github.io/pypi/
|
|
115
116
|
> pip install "pydantic>=2.12.4,<2.13"
|
|
117
|
+
> pkg install python-cryptography
|
|
116
118
|
> ```
|
|
117
119
|
|
|
118
120
|
That's it! You're all set to start chunking.
|
|
@@ -132,6 +134,8 @@ Want to unlock more Chunklet-py superpowers? Add these optional dependencies bas
|
|
|
132
134
|
* **Visualization:** For the interactive web-based chunk visualizer:
|
|
133
135
|
```bash
|
|
134
136
|
pip install "chunklet-py[visualization]"
|
|
137
|
+
# Or
|
|
138
|
+
pip install "chunklet-py[viz]"
|
|
135
139
|
```
|
|
136
140
|
* **All Extras:** To install all optional dependencies:
|
|
137
141
|
```bash
|
|
@@ -267,6 +271,13 @@ splitter = SentenceSplitter()
|
|
|
267
271
|
sentences = splitter.split_text(text, lang="auto")
|
|
268
272
|
```
|
|
269
273
|
|
|
274
|
+
> [!TIP]
|
|
275
|
+
> **Advanced text splitting**
|
|
276
|
+
>
|
|
277
|
+
> Chunklet-py quietly delegates sentence splitting to [yasbd-lib](https://github.com/speedyk-005/yasbd-lib), our high-accuracy SBD supporting **39 languages**.
|
|
278
|
+
>
|
|
279
|
+
> Curious why we built it? Read **[yasbd-lib vs PySBD: Two Philosophies of Sentence Boundary Detection](https://dev.to/speed_k_7e1b449706e59e433/yasbd-lib-vs-pysbd-two-philosophies-of-sentence-boundary-detection-i88)**.
|
|
280
|
+
|
|
270
281
|
**CLI (Command Line Interface)**
|
|
271
282
|
|
|
272
283
|
If you prefer the terminal to an IDE, the CLI is packed with features. Just ask for help.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "chunklet-py"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.4.0"
|
|
4
4
|
description = "High-fidelity context-aware chunking and interactive visualization for RAG. Advanced segmentation for code and documents, because your LLM is only as smart as the fragments you feed it."
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "speedyk_005", email = "speedy40115719@gmail.com" }
|
|
@@ -26,17 +26,16 @@ classifiers = [
|
|
|
26
26
|
]
|
|
27
27
|
|
|
28
28
|
keywords = [
|
|
29
|
-
"nlp", "chunking", "text-splitting", "sentence-splitting", "llm", "rag", "ai", "multilingual",
|
|
29
|
+
"nlp", "chunking", "chunker", "text-chunking", "text-splitting", "sentence-splitting", "llm", "rag", "ai", "multilingual",
|
|
30
30
|
"text processing", "natural language processing", "data processing",
|
|
31
31
|
"information retrieval", "semantic search", "document processing", "document-chunker",
|
|
32
|
-
"code chunking", "code-chunker", "
|
|
32
|
+
"code chunking", "code-chunker", "programming languages",
|
|
33
33
|
"code structure", "chunk-visualization",
|
|
34
34
|
]
|
|
35
35
|
|
|
36
36
|
dependencies = [
|
|
37
37
|
"loguru>=0.7.3,<1.0",
|
|
38
|
-
"
|
|
39
|
-
"sentsplit>=1.0.8,<2.0",
|
|
38
|
+
"yasbd-lib>=0.12.0,<2.0",
|
|
40
39
|
|
|
41
40
|
# Rust binding (1.0+) for faster performance on supported platforms, legacy for Android temporarily.
|
|
42
41
|
"sentencex<=0.6.1; sys_platform == 'linux' and (platform_machine == 'aarch64' or platform_machine == 'armv7l')",
|
|
@@ -45,15 +44,11 @@ dependencies = [
|
|
|
45
44
|
"indic-nlp-library>=0.92,<1.0",
|
|
46
45
|
"py3langid>=0.3.0,<1.0",
|
|
47
46
|
"mpire>=2.10.2,<3.0",
|
|
48
|
-
"dotdict3>=1.1.0,<2.0",
|
|
49
47
|
"more-itertools>=10.6.0,<12.0",
|
|
50
48
|
"regex>=2025.7.29",
|
|
51
49
|
"pydantic>=2.11.0,<3.0",
|
|
52
50
|
"typer>=0.19.0,<1.0",
|
|
53
51
|
"charset-normalizer>=3.4.2,<4.0",
|
|
54
|
-
|
|
55
|
-
# sentsplit depends on deprecated pkg_resources removed in setuptools 81.0.0
|
|
56
|
-
"setuptools<81",
|
|
57
52
|
]
|
|
58
53
|
|
|
59
54
|
[project.optional-dependencies]
|
|
@@ -65,13 +60,14 @@ structured-document = [
|
|
|
65
60
|
"docutils>=0.21.2,<1.0",
|
|
66
61
|
"markdownify>=1.1.0,<2.0",
|
|
67
62
|
"striprtf>=0.0.29,<1.0",
|
|
63
|
+
"mailparse>=1.0.1,<2.0",
|
|
64
|
+
"python-pptx>=1.0.0,<2.0",
|
|
68
65
|
|
|
69
66
|
# Lastest version of 2.x.x series
|
|
70
|
-
"pylatexenc==2.
|
|
67
|
+
"pylatexenc==2.11",
|
|
71
68
|
|
|
72
69
|
"odfpy>=1.4.1,<2.0",
|
|
73
70
|
"openpyxl>=3.1.5,<4.0",
|
|
74
|
-
"tabulate2>=1.10.2,<2.0",
|
|
75
71
|
]
|
|
76
72
|
|
|
77
73
|
# Kept for compatibility
|
|
@@ -100,7 +96,6 @@ dev = [
|
|
|
100
96
|
"pytest>=8.3.5",
|
|
101
97
|
"pytest-cov>=6.2.1",
|
|
102
98
|
"pytest-mock>=3.14.1",
|
|
103
|
-
"pytest-timeout>=2.3.1",
|
|
104
99
|
"ruff>=0.14.14",
|
|
105
100
|
]
|
|
106
101
|
|
|
@@ -152,14 +147,13 @@ chunklet = ["visualizer/static/**/*"]
|
|
|
152
147
|
minversion = "7.4"
|
|
153
148
|
filterwarnings = [
|
|
154
149
|
"ignore::DeprecationWarning:websockets.*",
|
|
155
|
-
"ignore::DeprecationWarning:
|
|
150
|
+
"ignore::DeprecationWarning:multiprocessing.*",
|
|
156
151
|
]
|
|
157
152
|
|
|
158
153
|
# disable coverage report to prevent racing with multiprocessing
|
|
159
154
|
# addopts = "--cov=chunklet --cov-report=term-missing:skip-covered --durations=10"
|
|
160
|
-
addopts = "--
|
|
161
|
-
|
|
162
|
-
testpaths = ["tests"]
|
|
155
|
+
addopts = "--doctest-modules"
|
|
156
|
+
testpaths = ["tests", "src/chunklet/common/dotdict.py"]
|
|
163
157
|
python_files = "test_*.py"
|
|
164
158
|
|
|
165
159
|
[tool.coverage.run]
|
|
@@ -170,7 +164,6 @@ omit = [
|
|
|
170
164
|
"src/chunklet/**/__init__.py",
|
|
171
165
|
]
|
|
172
166
|
|
|
173
|
-
|
|
174
167
|
[tool.ruff]
|
|
175
168
|
target-version = "py310"
|
|
176
169
|
line-length = 88
|
|
@@ -194,9 +187,6 @@ ignore = ["E501", "E203"]
|
|
|
194
187
|
# Ignore warnings about File(...)
|
|
195
188
|
"src/chunklet/visualizer/visualizer.py" = ["B008"]
|
|
196
189
|
|
|
197
|
-
# Ignore E402 - warnings.filterwarnings() must be called before imports to suppress pkg_resources
|
|
198
|
-
"src/chunklet/sentence_splitter/sentence_splitter.py" = ["E402"]
|
|
199
|
-
|
|
200
190
|
[tool.ruff.format]
|
|
201
191
|
# Replaces Black exclude logic
|
|
202
192
|
exclude = [
|
|
@@ -328,7 +328,7 @@ def split_command(
|
|
|
328
328
|
destination.write_text(output_str, encoding="utf-8")
|
|
329
329
|
typer.echo(
|
|
330
330
|
f"Successfully split and wrote {len(sentences)} sentences "
|
|
331
|
-
f"{source_display} to {destination} (Confidence: {confidence})",
|
|
331
|
+
f"{source_display} to {destination} (Lang: {lang_detected}, Confidence: {confidence})",
|
|
332
332
|
err=True,
|
|
333
333
|
)
|
|
334
334
|
except Exception as e:
|
|
@@ -339,7 +339,7 @@ def split_command(
|
|
|
339
339
|
|
|
340
340
|
typer.echo(
|
|
341
341
|
f"--- Sentences ({len(sentences)}): "
|
|
342
|
-
f" [{source_display} | Lang: {
|
|
342
|
+
f" [{source_display} | Lang: {lang_detected} | Confidence: {confidence}] ---"
|
|
343
343
|
)
|
|
344
344
|
|
|
345
345
|
for sentence in sentences:
|
{chunklet_py-2.3.1 → chunklet_py-2.4.0}/src/chunklet/code_chunker/_code_structure_extractor.py
RENAMED
|
@@ -19,7 +19,6 @@ try:
|
|
|
19
19
|
except ImportError: # pragma: no cover
|
|
20
20
|
Node, ET = None, None
|
|
21
21
|
|
|
22
|
-
from loguru import logger
|
|
23
22
|
|
|
24
23
|
from chunklet.code_chunker.patterns import (
|
|
25
24
|
ALL_SINGLE_LINE_COMM,
|
|
@@ -34,9 +33,8 @@ from chunklet.code_chunker.patterns import (
|
|
|
34
33
|
NAMESPACE_DECLARATION,
|
|
35
34
|
OPENER,
|
|
36
35
|
)
|
|
37
|
-
from chunklet.common.validation import validate_input
|
|
38
36
|
from chunklet.common.logging_utils import log_info
|
|
39
|
-
|
|
37
|
+
from chunklet.common.validation import validate_input
|
|
40
38
|
|
|
41
39
|
CodeLine = namedtuple(
|
|
42
40
|
"CodeLine", ["line_number", "content", "indent_level", "func_partial_signature"]
|
|
@@ -316,7 +314,9 @@ class CodeStructureExtractor:
|
|
|
316
314
|
if not (curr_struct or annotated_lines_buffer):
|
|
317
315
|
return
|
|
318
316
|
|
|
319
|
-
candidates = [
|
|
317
|
+
candidates = [
|
|
318
|
+
entry for v in annotated_lines_buffer.values() for entry in v
|
|
319
|
+
] + curr_struct
|
|
320
320
|
sorted_candidates = sorted(candidates, key=lambda x: x.line_number)
|
|
321
321
|
|
|
322
322
|
if not sorted_candidates:
|
|
@@ -372,13 +372,18 @@ class CodeStructureExtractor:
|
|
|
372
372
|
indent_level = len(deannotated_line) - len(deannotated_line.lstrip())
|
|
373
373
|
first_metadata = tag == "META" and not annotated_lines_buffer["META"]
|
|
374
374
|
consecutive_docstrings = (
|
|
375
|
-
annotated_lines_buffer["DOC"]
|
|
375
|
+
annotated_lines_buffer["DOC"]
|
|
376
|
+
and annotated_lines_buffer["DOC"][-1].line_number == line_no - 1
|
|
376
377
|
)
|
|
377
378
|
|
|
378
379
|
if first_metadata or not consecutive_docstrings:
|
|
379
|
-
self._flush_snippet(
|
|
380
|
+
self._flush_snippet(
|
|
381
|
+
state["curr_struct"], state["snippet_dicts"], annotated_lines_buffer
|
|
382
|
+
)
|
|
380
383
|
|
|
381
|
-
annotated_lines_buffer[tag].append(
|
|
384
|
+
annotated_lines_buffer[tag].append(
|
|
385
|
+
CodeLine(line_no, deannotated_line, indent_level, None)
|
|
386
|
+
)
|
|
382
387
|
|
|
383
388
|
def _handle_block_start(
|
|
384
389
|
self,
|
|
@@ -431,12 +436,16 @@ class CodeStructureExtractor:
|
|
|
431
436
|
if state["curr_struct"]:
|
|
432
437
|
if is_python_code:
|
|
433
438
|
self._flush_snippet(
|
|
434
|
-
state["curr_struct"],
|
|
439
|
+
state["curr_struct"],
|
|
440
|
+
state["snippet_dicts"],
|
|
441
|
+
annotated_lines_buffer,
|
|
435
442
|
)
|
|
436
443
|
else:
|
|
437
444
|
doc = annotated_lines_buffer.pop("DOC", [])
|
|
438
445
|
self._flush_snippet(
|
|
439
|
-
state["curr_struct"],
|
|
446
|
+
state["curr_struct"],
|
|
447
|
+
state["snippet_dicts"],
|
|
448
|
+
annotated_lines_buffer,
|
|
440
449
|
)
|
|
441
450
|
annotated_lines_buffer.clear()
|
|
442
451
|
annotated_lines_buffer["doc"] = doc
|
|
@@ -536,7 +545,9 @@ class CodeStructureExtractor:
|
|
|
536
545
|
|
|
537
546
|
# Append last snippet
|
|
538
547
|
if state["curr_struct"]:
|
|
539
|
-
self._flush_snippet(
|
|
548
|
+
self._flush_snippet(
|
|
549
|
+
state["curr_struct"], state["snippet_dicts"], annotated_lines_buffer
|
|
550
|
+
)
|
|
540
551
|
|
|
541
552
|
snippet_dicts = self._post_processing(state["snippet_dicts"])
|
|
542
553
|
log_info(
|
|
@@ -29,10 +29,11 @@ from itertools import chain
|
|
|
29
29
|
from pathlib import Path
|
|
30
30
|
from typing import Annotated, Any, Callable, Generator, Literal
|
|
31
31
|
|
|
32
|
-
from dotdict3 import DotDict
|
|
33
32
|
from more_itertools import unique_everseen
|
|
34
33
|
from pydantic import Field
|
|
35
34
|
|
|
35
|
+
from chunklet.common.dotdict import DotDict
|
|
36
|
+
|
|
36
37
|
try:
|
|
37
38
|
import defusedxml.ElementTree as ET
|
|
38
39
|
from charset_normalizer import from_path
|
|
@@ -50,7 +51,7 @@ from chunklet.common.deprecation import deprecated_callable
|
|
|
50
51
|
from chunklet.common.logging_utils import log_info
|
|
51
52
|
from chunklet.common.path_utils import is_path_like, read_text_file
|
|
52
53
|
from chunklet.common.token_utils import count_tokens
|
|
53
|
-
from chunklet.common.validation import
|
|
54
|
+
from chunklet.common.validation import IterableOfPath, IterableOfStr, validate_input
|
|
54
55
|
from chunklet.exceptions import (
|
|
55
56
|
InvalidInputError,
|
|
56
57
|
MissingTokenCounterError,
|
|
@@ -125,6 +126,7 @@ class CodeChunker(BaseChunker):
|
|
|
125
126
|
# Deduplicate relations
|
|
126
127
|
def relation_key(relation: dict):
|
|
127
128
|
return tuple(sorted(relation.items()))
|
|
129
|
+
|
|
128
130
|
unique_relations = list(unique_everseen(all_relations_flat, key=relation_key))
|
|
129
131
|
|
|
130
132
|
if not unique_relations:
|
|
@@ -203,7 +205,7 @@ class CodeChunker(BaseChunker):
|
|
|
203
205
|
}
|
|
204
206
|
)
|
|
205
207
|
)
|
|
206
|
-
curr_chunk = [
|
|
208
|
+
curr_chunk = []
|
|
207
209
|
token_count = 0
|
|
208
210
|
line_count = 0
|
|
209
211
|
|
|
@@ -255,7 +257,6 @@ class CodeChunker(BaseChunker):
|
|
|
255
257
|
Args:
|
|
256
258
|
box_tokens: Actual token count in the block
|
|
257
259
|
max_tokens: Maximum allowed tokens
|
|
258
|
-
box_lines: Actual line count in the block
|
|
259
260
|
max_lines: Maximum allowed lines
|
|
260
261
|
function_count: Actual function count in the block
|
|
261
262
|
max_functions: Maximum allowed functions
|
|
@@ -278,6 +279,62 @@ class CodeChunker(BaseChunker):
|
|
|
278
279
|
f"Content starting with: \n```\n{content_preview}...\n```"
|
|
279
280
|
)
|
|
280
281
|
|
|
282
|
+
def _handle_oversized_snippet(
|
|
283
|
+
self,
|
|
284
|
+
snippet_dict: dict,
|
|
285
|
+
**kwargs: Any,
|
|
286
|
+
) -> None:
|
|
287
|
+
"""
|
|
288
|
+
Handle a snippet that exceeds constraints and cannot be merged.
|
|
289
|
+
|
|
290
|
+
Expects ``kwargs`` to contain the same keys as the local variables of
|
|
291
|
+
``_group_by_chunk`` that are relevant to oversized handling:
|
|
292
|
+
``max_tokens``, ``max_lines``, ``max_functions``, ``strict``,
|
|
293
|
+
``source``, ``token_counter``, ``cumulative_lengths``,
|
|
294
|
+
``result_chunks``, ``box_tokens``, ``box_lines``, ``is_func``.
|
|
295
|
+
|
|
296
|
+
Either raises a ``TokenLimitError`` (strict mode) or splits the
|
|
297
|
+
oversized block into sub-chunks appended to ``result_chunks``.
|
|
298
|
+
"""
|
|
299
|
+
limit_msg = self._format_limit_msg(
|
|
300
|
+
kwargs.get("box_tokens", 0),
|
|
301
|
+
kwargs["max_tokens"],
|
|
302
|
+
kwargs.get("box_lines", 0),
|
|
303
|
+
kwargs["max_lines"],
|
|
304
|
+
int(kwargs.get("is_func", False)),
|
|
305
|
+
kwargs["max_functions"],
|
|
306
|
+
snippet_dict["content"][:100],
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
strict = kwargs["strict"]
|
|
310
|
+
result_chunks = kwargs["result_chunks"]
|
|
311
|
+
|
|
312
|
+
if strict:
|
|
313
|
+
raise TokenLimitError(
|
|
314
|
+
f"Structural block exceeds maximum limit.\n{limit_msg}\n"
|
|
315
|
+
"Reason: Prevent splitting inside interest points (function, class, region, ...)\n"
|
|
316
|
+
"💡Hint: Consider increasing 'max_tokens', 'max_lines', or 'max_functions', "
|
|
317
|
+
"refactoring the oversized block, or setting 'strict=False' to allow automatic splitting of oversized blocks."
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
logger.warning(
|
|
321
|
+
"Splitting oversized block into sub-chunks.\n(%s)",
|
|
322
|
+
limit_msg,
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
sub_chunks = self._split_oversized(
|
|
326
|
+
snippet_dict,
|
|
327
|
+
kwargs["max_tokens"],
|
|
328
|
+
kwargs["max_lines"],
|
|
329
|
+
kwargs["source"],
|
|
330
|
+
kwargs["token_counter"],
|
|
331
|
+
kwargs["cumulative_lengths"],
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
for sub_chunk in sub_chunks:
|
|
335
|
+
sub_chunk.metadata.chunk_num = len(result_chunks) + 1
|
|
336
|
+
result_chunks.append(sub_chunk)
|
|
337
|
+
|
|
281
338
|
def _group_by_chunk(
|
|
282
339
|
self,
|
|
283
340
|
snippet_dicts: list[dict],
|
|
@@ -359,41 +416,23 @@ class CodeChunker(BaseChunker):
|
|
|
359
416
|
|
|
360
417
|
elif not merged_content:
|
|
361
418
|
# Too big and nothing merged yet: handle oversize
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
419
|
+
self._handle_oversized_snippet(
|
|
420
|
+
snippet_dict,
|
|
421
|
+
**{
|
|
422
|
+
"max_tokens": max_tokens,
|
|
423
|
+
"max_lines": max_lines,
|
|
424
|
+
"max_functions": max_functions,
|
|
425
|
+
"strict": strict,
|
|
426
|
+
"source": source,
|
|
427
|
+
"token_counter": token_counter,
|
|
428
|
+
"cumulative_lengths": cumulative_lengths,
|
|
429
|
+
"result_chunks": result_chunks,
|
|
430
|
+
"box_tokens": box_tokens,
|
|
431
|
+
"box_lines": box_lines,
|
|
432
|
+
"is_func": is_function,
|
|
433
|
+
},
|
|
370
434
|
)
|
|
371
|
-
|
|
372
|
-
raise TokenLimitError(
|
|
373
|
-
f"Structural block exceeds maximum limit.\n{limit_msg}\n"
|
|
374
|
-
"Reason: Prevent splitting inside interest points (function, class, region, ...)\n"
|
|
375
|
-
"💡Hint: Consider increasing 'max_tokens', 'max_lines', or 'max_functions', "
|
|
376
|
-
"refactoring the oversized block, or setting 'strict=False' to allow automatic splitting of oversized blocks."
|
|
377
|
-
)
|
|
378
|
-
else: # Else split further
|
|
379
|
-
logger.warning(
|
|
380
|
-
"Splitting oversized block into sub-chunks.\n(%s)",
|
|
381
|
-
limit_msg,
|
|
382
|
-
)
|
|
383
|
-
|
|
384
|
-
sub_chunks = self._split_oversized(
|
|
385
|
-
snippet_dict,
|
|
386
|
-
max_tokens,
|
|
387
|
-
max_lines,
|
|
388
|
-
source,
|
|
389
|
-
token_counter,
|
|
390
|
-
cumulative_lengths,
|
|
391
|
-
)
|
|
392
|
-
|
|
393
|
-
for sub_chunk in sub_chunks:
|
|
394
|
-
sub_chunk.metadata.chunk_num = len(result_chunks) + 1
|
|
395
|
-
result_chunks.append(sub_chunk)
|
|
396
|
-
index += 1
|
|
435
|
+
index += 1
|
|
397
436
|
else:
|
|
398
437
|
# Flush current merged content as a chunk
|
|
399
438
|
start_span = cumulative_lengths[start_line - 1]
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import warnings
|
|
3
3
|
from importlib.metadata import PackageNotFoundError, version
|
|
4
|
-
from packaging.version import InvalidVersion, Version
|
|
5
4
|
from typing import Any, Callable
|
|
6
5
|
|
|
6
|
+
from packaging.version import Version
|
|
7
|
+
|
|
7
8
|
try:
|
|
8
9
|
CURRENT_VERSION = version("chunklet-py")
|
|
9
10
|
except PackageNotFoundError: # pragma: no cover
|