chunklet-py 2.3.2__tar.gz → 2.4.1__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.2/src/chunklet_py.egg-info → chunklet_py-2.4.1}/PKG-INFO +23 -14
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/README.md +17 -6
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/pyproject.toml +10 -19
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/base_chunker.py +0 -1
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/cli.py +2 -2
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/code_chunker/_code_structure_extractor.py +21 -10
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/code_chunker/code_chunker.py +77 -38
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/common/deprecation.py +2 -1
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/common/dotdict.py +72 -2
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/common/path_utils.py +1 -2
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/common/validation.py +12 -11
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/_plain_text_chunker.py +7 -5
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/converters/rst_2_md.py +1 -3
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/converters/table_2_md.py +2 -17
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/document_chunker.py +20 -8
- chunklet_py-2.4.1/src/chunklet/document_chunker/md_table.py +36 -0
- chunklet_py-2.4.1/src/chunklet/document_chunker/processors/eml_processor.py +117 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/processors/epub_processor.py +1 -1
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/processors/odt_processor.py +0 -1
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/processors/pdf_processor.py +1 -3
- chunklet_py-2.4.1/src/chunklet/document_chunker/processors/pptx_processor.py +326 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/span_finder.py +9 -11
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/sentence_splitter/_universal_splitter.py +8 -10
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/sentence_splitter/languages.py +10 -40
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/sentence_splitter/sentence_splitter.py +13 -20
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/sentence_splitter/terminators.py +0 -2
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/visualizer/static/index.html +2 -1
- {chunklet_py-2.3.2 → chunklet_py-2.4.1/src/chunklet_py.egg-info}/PKG-INFO +23 -14
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet_py.egg-info/SOURCES.txt +3 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet_py.egg-info/requires.txt +4 -6
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/tests/test_document_chunking.py +3 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/tests/test_plain_text_chunking.py +5 -3
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/tests/test_sentence_splitting.py +21 -28
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/tests/test_visualization.py +4 -4
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/LICENSE +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/setup.cfg +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/__init__.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/code_chunker/__init__.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/code_chunker/patterns.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/code_chunker/utils.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/common/__init__.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/common/batch_runner.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/common/logging_utils.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/common/token_utils.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/__init__.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/converters/__init__.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/converters/html_2_md.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/converters/latex_2_md.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/processors/__init__.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/processors/base_processor.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/processors/docx_processor.py +1 -1
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/document_chunker/registry.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/exceptions.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/sentence_splitter/__init__.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/sentence_splitter/registry.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/visualizer/__init__.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/visualizer/static/css/style.css +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/visualizer/static/js/app.js +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/visualizer/static/js/utils.js +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet/visualizer/visualizer.py +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet_py.egg-info/dependency_links.txt +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet_py.egg-info/entry_points.txt +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/src/chunklet_py.egg-info/top_level.txt +0 -0
- {chunklet_py-2.3.2 → chunklet_py-2.4.1}/tests/test_code_chunking.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chunklet-py
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.1
|
|
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,8 +26,7 @@ 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
|
|
@@ -38,7 +37,6 @@ Requires-Dist: regex>=2025.7.29
|
|
|
38
37
|
Requires-Dist: pydantic<3.0,>=2.11.0
|
|
39
38
|
Requires-Dist: typer<1.0,>=0.19.0
|
|
40
39
|
Requires-Dist: charset-normalizer<4.0,>=3.4.2
|
|
41
|
-
Requires-Dist: setuptools<81
|
|
42
40
|
Provides-Extra: structured-document
|
|
43
41
|
Requires-Dist: pdfminer.six>=20250324; extra == "structured-document"
|
|
44
42
|
Requires-Dist: python-docx<2.0,>=1.2.0; extra == "structured-document"
|
|
@@ -47,10 +45,11 @@ Requires-Dist: ebooklib<1.0,>=0.19; extra == "structured-document"
|
|
|
47
45
|
Requires-Dist: docutils<1.0,>=0.21.2; extra == "structured-document"
|
|
48
46
|
Requires-Dist: markdownify<2.0,>=1.1.0; extra == "structured-document"
|
|
49
47
|
Requires-Dist: striprtf<1.0,>=0.0.29; extra == "structured-document"
|
|
50
|
-
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"
|
|
51
51
|
Requires-Dist: odfpy<2.0,>=1.4.1; extra == "structured-document"
|
|
52
52
|
Requires-Dist: openpyxl<4.0,>=3.1.5; extra == "structured-document"
|
|
53
|
-
Requires-Dist: tabulate2<2.0,>=1.10.2; extra == "structured-document"
|
|
54
53
|
Provides-Extra: document
|
|
55
54
|
Requires-Dist: chunklet-py[structured-document]; extra == "document"
|
|
56
55
|
Provides-Extra: code
|
|
@@ -70,7 +69,6 @@ Provides-Extra: dev
|
|
|
70
69
|
Requires-Dist: pytest>=8.3.5; extra == "dev"
|
|
71
70
|
Requires-Dist: pytest-cov>=6.2.1; extra == "dev"
|
|
72
71
|
Requires-Dist: pytest-mock>=3.14.1; extra == "dev"
|
|
73
|
-
Requires-Dist: pytest-timeout>=2.3.1; extra == "dev"
|
|
74
72
|
Requires-Dist: ruff>=0.14.14; extra == "dev"
|
|
75
73
|
Provides-Extra: docs
|
|
76
74
|
Requires-Dist: mike>=2.1.2; extra == "docs"
|
|
@@ -128,7 +126,7 @@ Dumb splitting causes problems:
|
|
|
128
126
|
Smart chunking solves this by:
|
|
129
127
|
|
|
130
128
|
- **Smart limits** — Respects both natural boundaries (sentences, paragraphs, sections) AND configurable limits (tokens, lines, functions)
|
|
131
|
-
- **Language-aware** — Detects language automatically and applies the right rules (
|
|
129
|
+
- **Language-aware** — Detects language automatically and applies the right rules (60+ languages supported)
|
|
132
130
|
- **Context preservation** — Overlap between chunks, rich metadata (source, span, document structure)
|
|
133
131
|
|
|
134
132
|
## 🤔 So What's Chunklet-py Anyway? (And Why Should You Care?)
|
|
@@ -157,10 +155,10 @@ Perfect for prepping data for LLMs, building RAG systems, or powering AI search
|
|
|
157
155
|
| 🪶 **Featherlight Footprint** | Designed to be lightweight and memory-efficient, ensuring optimal performance without unnecessary overhead. |
|
|
158
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. |
|
|
159
157
|
| 🔧 **Infinitely Customizable** | Offers extensive customization options, from pluggable token counters to custom sentence splitters and processors. |
|
|
160
|
-
| 🌐 **Multilingual Mastery** | Supports over
|
|
158
|
+
| 🌐 **Multilingual Mastery** | Supports over 60 natural languages for text and document chunking with intelligent detection and language-specific algorithms. |
|
|
161
159
|
| 🧑💻 **Code-Aware Intelligence** | Language-agnostic code chunking that understands and preserves the structural integrity of your source code. |
|
|
162
160
|
| 🎯 **Precision Chunking** | Flexible chunking with configurable limits based on sentences, tokens, sections, lines, and functions. |
|
|
163
|
-
| 📄 **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`. |
|
|
161
|
+
| 📄 **Document Format Mastery** | Processes a wide array of document formats including `.pdf`, `.docx`, `.epub`, `.eml`, `.pptx`, `.txt`, `.tex`, `.html`, `.hml`, `.md`, `.rst`, `.rtf`, `.odt`, `.csv`, and `.xlsx`. |
|
|
164
162
|
| 💻 **Triple Interface: CLI, Library & Web** | Use it as a command-line tool, import as a library for deep integration, or launch the interactive web visualizer for real-time chunk exploration and parameter tuning. |
|
|
165
163
|
|
|
166
164
|
|
|
@@ -184,19 +182,21 @@ The simplest way to get started is with pip:
|
|
|
184
182
|
|
|
185
183
|
```bash
|
|
186
184
|
# Install and check it's working
|
|
187
|
-
pip install chunklet-py
|
|
185
|
+
pip install chunklet-py -U
|
|
188
186
|
chunklet --version
|
|
189
187
|
```
|
|
190
188
|
|
|
191
189
|
> [!TIP]
|
|
192
190
|
> <b>Termux (Android)</b>
|
|
193
191
|
>
|
|
194
|
-
> 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:
|
|
195
194
|
>
|
|
196
195
|
> ```bash
|
|
197
196
|
> pip install typing-extensions
|
|
198
197
|
> pip install pydantic-core --index-url https://termux-user-repository.github.io/pypi/
|
|
199
198
|
> pip install "pydantic>=2.12.4,<2.13"
|
|
199
|
+
> pkg install python-cryptography
|
|
200
200
|
> ```
|
|
201
201
|
|
|
202
202
|
That's it! You're all set to start chunking.
|
|
@@ -205,7 +205,7 @@ That's it! You're all set to start chunking.
|
|
|
205
205
|
|
|
206
206
|
Want to unlock more Chunklet-py superpowers? Add these optional dependencies based on what you need:
|
|
207
207
|
|
|
208
|
-
* **Structured Documents:** For handling `.pdf`, `.docx`, `.epub`, and other document formats:
|
|
208
|
+
* **Structured Documents:** For handling `.pdf`, `.docx`, `.epub`, `.eml`, `.pptx`, and other document formats:
|
|
209
209
|
```bash
|
|
210
210
|
pip install "chunklet-py[structured-document]"
|
|
211
211
|
```
|
|
@@ -216,6 +216,8 @@ Want to unlock more Chunklet-py superpowers? Add these optional dependencies bas
|
|
|
216
216
|
* **Visualization:** For the interactive web-based chunk visualizer:
|
|
217
217
|
```bash
|
|
218
218
|
pip install "chunklet-py[visualization]"
|
|
219
|
+
# Or
|
|
220
|
+
pip install "chunklet-py[viz]"
|
|
219
221
|
```
|
|
220
222
|
* **All Extras:** To install all optional dependencies:
|
|
221
223
|
```bash
|
|
@@ -351,6 +353,13 @@ splitter = SentenceSplitter()
|
|
|
351
353
|
sentences = splitter.split_text(text, lang="auto")
|
|
352
354
|
```
|
|
353
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
|
+
|
|
354
363
|
**CLI (Command Line Interface)**
|
|
355
364
|
|
|
356
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,10 +73,10 @@ 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
|
-
| 📄 **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`. |
|
|
79
|
+
| 📄 **Document Format Mastery** | Processes a wide array of document formats including `.pdf`, `.docx`, `.epub`, `.eml`, `.pptx`, `.txt`, `.tex`, `.html`, `.hml`, `.md`, `.rst`, `.rtf`, `.odt`, `.csv`, and `.xlsx`. |
|
|
80
80
|
| 💻 **Triple Interface: CLI, Library & Web** | Use it as a command-line tool, import as a library for deep integration, or launch the interactive web visualizer for real-time chunk exploration and parameter tuning. |
|
|
81
81
|
|
|
82
82
|
|
|
@@ -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.
|
|
@@ -121,7 +123,7 @@ That's it! You're all set to start chunking.
|
|
|
121
123
|
|
|
122
124
|
Want to unlock more Chunklet-py superpowers? Add these optional dependencies based on what you need:
|
|
123
125
|
|
|
124
|
-
* **Structured Documents:** For handling `.pdf`, `.docx`, `.epub`, and other document formats:
|
|
126
|
+
* **Structured Documents:** For handling `.pdf`, `.docx`, `.epub`, `.eml`, `.pptx`, and other document formats:
|
|
125
127
|
```bash
|
|
126
128
|
pip install "chunklet-py[structured-document]"
|
|
127
129
|
```
|
|
@@ -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.1"
|
|
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')",
|
|
@@ -50,9 +49,6 @@ dependencies = [
|
|
|
50
49
|
"pydantic>=2.11.0,<3.0",
|
|
51
50
|
"typer>=0.19.0,<1.0",
|
|
52
51
|
"charset-normalizer>=3.4.2,<4.0",
|
|
53
|
-
|
|
54
|
-
# sentsplit depends on deprecated pkg_resources removed in setuptools 81.0.0
|
|
55
|
-
"setuptools<81",
|
|
56
52
|
]
|
|
57
53
|
|
|
58
54
|
[project.optional-dependencies]
|
|
@@ -64,13 +60,14 @@ structured-document = [
|
|
|
64
60
|
"docutils>=0.21.2,<1.0",
|
|
65
61
|
"markdownify>=1.1.0,<2.0",
|
|
66
62
|
"striprtf>=0.0.29,<1.0",
|
|
63
|
+
"mailparse>=1.0.1,<2.0",
|
|
64
|
+
"python-pptx>=1.0.0,<2.0",
|
|
67
65
|
|
|
68
66
|
# Lastest version of 2.x.x series
|
|
69
|
-
"pylatexenc==2.
|
|
67
|
+
"pylatexenc==2.11",
|
|
70
68
|
|
|
71
69
|
"odfpy>=1.4.1,<2.0",
|
|
72
70
|
"openpyxl>=3.1.5,<4.0",
|
|
73
|
-
"tabulate2>=1.10.2,<2.0",
|
|
74
71
|
]
|
|
75
72
|
|
|
76
73
|
# Kept for compatibility
|
|
@@ -99,7 +96,6 @@ dev = [
|
|
|
99
96
|
"pytest>=8.3.5",
|
|
100
97
|
"pytest-cov>=6.2.1",
|
|
101
98
|
"pytest-mock>=3.14.1",
|
|
102
|
-
"pytest-timeout>=2.3.1",
|
|
103
99
|
"ruff>=0.14.14",
|
|
104
100
|
]
|
|
105
101
|
|
|
@@ -151,14 +147,13 @@ chunklet = ["visualizer/static/**/*"]
|
|
|
151
147
|
minversion = "7.4"
|
|
152
148
|
filterwarnings = [
|
|
153
149
|
"ignore::DeprecationWarning:websockets.*",
|
|
154
|
-
"ignore::DeprecationWarning:
|
|
150
|
+
"ignore::DeprecationWarning:multiprocessing.*",
|
|
155
151
|
]
|
|
156
152
|
|
|
157
153
|
# disable coverage report to prevent racing with multiprocessing
|
|
158
154
|
# addopts = "--cov=chunklet --cov-report=term-missing:skip-covered --durations=10"
|
|
159
|
-
addopts = "--
|
|
160
|
-
|
|
161
|
-
testpaths = ["tests"]
|
|
155
|
+
addopts = "--doctest-modules"
|
|
156
|
+
testpaths = ["tests", "src/chunklet/common/dotdict.py"]
|
|
162
157
|
python_files = "test_*.py"
|
|
163
158
|
|
|
164
159
|
[tool.coverage.run]
|
|
@@ -169,7 +164,6 @@ omit = [
|
|
|
169
164
|
"src/chunklet/**/__init__.py",
|
|
170
165
|
]
|
|
171
166
|
|
|
172
|
-
|
|
173
167
|
[tool.ruff]
|
|
174
168
|
target-version = "py310"
|
|
175
169
|
line-length = 88
|
|
@@ -193,9 +187,6 @@ ignore = ["E501", "E203"]
|
|
|
193
187
|
# Ignore warnings about File(...)
|
|
194
188
|
"src/chunklet/visualizer/visualizer.py" = ["B008"]
|
|
195
189
|
|
|
196
|
-
# Ignore E402 - warnings.filterwarnings() must be called before imports to suppress pkg_resources
|
|
197
|
-
"src/chunklet/sentence_splitter/sentence_splitter.py" = ["E402"]
|
|
198
|
-
|
|
199
190
|
[tool.ruff.format]
|
|
200
191
|
# Replaces Black exclude logic
|
|
201
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.2 → chunklet_py-2.4.1}/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 chunklet.common.dotdict 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
|