mdkeychunker 0.3.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.
- mdkeychunker-0.3.0/.env.sample +25 -0
- mdkeychunker-0.3.0/CONTRIBUTING.md +238 -0
- mdkeychunker-0.3.0/LICENSE +21 -0
- mdkeychunker-0.3.0/MANIFEST.in +14 -0
- mdkeychunker-0.3.0/PKG-INFO +241 -0
- mdkeychunker-0.3.0/README.md +198 -0
- mdkeychunker-0.3.0/demo.md +206 -0
- mdkeychunker-0.3.0/examples.py +96 -0
- mdkeychunker-0.3.0/mdkeychunker/__init__.py +7 -0
- mdkeychunker-0.3.0/mdkeychunker/chunker.py +402 -0
- mdkeychunker-0.3.0/mdkeychunker/cli.py +63 -0
- mdkeychunker-0.3.0/mdkeychunker/config.py +40 -0
- mdkeychunker-0.3.0/mdkeychunker/enricher.py +201 -0
- mdkeychunker-0.3.0/mdkeychunker/llm_client.py +158 -0
- mdkeychunker-0.3.0/mdkeychunker/models.py +47 -0
- mdkeychunker-0.3.0/mdkeychunker/pipeline.py +86 -0
- mdkeychunker-0.3.0/mdkeychunker/restructurer.py +128 -0
- mdkeychunker-0.3.0/mdkeychunker.egg-info/PKG-INFO +241 -0
- mdkeychunker-0.3.0/mdkeychunker.egg-info/SOURCES.txt +29 -0
- mdkeychunker-0.3.0/mdkeychunker.egg-info/dependency_links.txt +1 -0
- mdkeychunker-0.3.0/mdkeychunker.egg-info/entry_points.txt +2 -0
- mdkeychunker-0.3.0/mdkeychunker.egg-info/requires.txt +23 -0
- mdkeychunker-0.3.0/mdkeychunker.egg-info/top_level.txt +1 -0
- mdkeychunker-0.3.0/pyproject.toml +81 -0
- mdkeychunker-0.3.0/setup.cfg +4 -0
- mdkeychunker-0.3.0/tests/__init__.py +8 -0
- mdkeychunker-0.3.0/tests/test_chunker.py +290 -0
- mdkeychunker-0.3.0/tests/test_enricher.py +292 -0
- mdkeychunker-0.3.0/tests/test_llm_client.py +194 -0
- mdkeychunker-0.3.0/tests/test_pipeline.py +237 -0
- mdkeychunker-0.3.0/tests/test_restructurer.py +262 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# === MDKeyChunker v2 Configuration ===
|
|
2
|
+
|
|
3
|
+
# LLM Provider: openai | anthropic | openai_compatible
|
|
4
|
+
LLM_PROVIDER=openai
|
|
5
|
+
|
|
6
|
+
# API key (not needed for local Ollama)
|
|
7
|
+
LLM_API_KEY=your_api_key_here
|
|
8
|
+
|
|
9
|
+
# Base URL — set for Ollama/vLLM/LM Studio (leave empty for OpenAI/Anthropic)
|
|
10
|
+
# LLM_BASE_URL=http://localhost:11434/v1
|
|
11
|
+
|
|
12
|
+
# Model name
|
|
13
|
+
LLM_MODEL=gpt-4o-mini
|
|
14
|
+
|
|
15
|
+
# Chunking
|
|
16
|
+
MIN_CHUNK_SIZE=100 # Minimum characters per chunk
|
|
17
|
+
MAX_CHUNK_SIZE=1500 # Soft max characters per chunk
|
|
18
|
+
|
|
19
|
+
# Key-based restructuring
|
|
20
|
+
MERGE_BY_KEYS=true # Merge chunks sharing the same key
|
|
21
|
+
MAX_MERGED_SIZE=3000 # Max combined size after merging
|
|
22
|
+
MIN_ORPHAN_SIZE=200 # Below this, orphan chunks get context enrichment
|
|
23
|
+
|
|
24
|
+
# Logging
|
|
25
|
+
LOG_LEVEL=INFO
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# Contributing to MDKeyChunker
|
|
2
|
+
|
|
3
|
+
Thank you for considering contributing to MDKeyChunker! This document provides guidelines and instructions for contributing.
|
|
4
|
+
|
|
5
|
+
## Development Setup
|
|
6
|
+
|
|
7
|
+
1. **Fork and clone the repository**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone https://github.com/bhavik-mangla/MDKeyChunker.git
|
|
11
|
+
cd MDKeyChunker
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
2. **Create a virtual environment**
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
python -m venv venv
|
|
18
|
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
3. **Install in development mode**
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pip install -e ".[dev]"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
4. **Run tests to verify setup**
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pytest
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Code Standards
|
|
34
|
+
|
|
35
|
+
### Style Guidelines
|
|
36
|
+
|
|
37
|
+
- **PEP 8**: Follow Python style guide
|
|
38
|
+
- **Type hints**: All functions must have type annotations
|
|
39
|
+
- **Docstrings**: Use Google-style docstrings
|
|
40
|
+
- **Line length**: 100 characters (configured in pyproject.toml)
|
|
41
|
+
|
|
42
|
+
### Tools
|
|
43
|
+
|
|
44
|
+
We use automated tools for code quality:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# Format code
|
|
48
|
+
ruff format mdkeychunker/
|
|
49
|
+
|
|
50
|
+
# Lint
|
|
51
|
+
ruff check mdkeychunker/
|
|
52
|
+
|
|
53
|
+
# Type checking
|
|
54
|
+
mypy mdkeychunker/
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Example Function
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
def process_chunks(text: str, config: Config) -> list[Chunk]:
|
|
61
|
+
"""
|
|
62
|
+
Process markdown text into enriched chunks.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
text: Input markdown text to process
|
|
66
|
+
config: Configuration object
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
List of enriched Chunk objects
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ValueError: If text is empty
|
|
73
|
+
"""
|
|
74
|
+
if not text:
|
|
75
|
+
raise ValueError("Text cannot be empty")
|
|
76
|
+
|
|
77
|
+
pipeline = Pipeline(config)
|
|
78
|
+
return pipeline.process_text(text)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Testing
|
|
82
|
+
|
|
83
|
+
### Writing Tests
|
|
84
|
+
|
|
85
|
+
- Place tests in `tests/` directory
|
|
86
|
+
- Name test files `test_*.py`
|
|
87
|
+
- Name test functions `test_*`
|
|
88
|
+
- Use fixtures for common setup
|
|
89
|
+
- Aim for >80% code coverage
|
|
90
|
+
|
|
91
|
+
### Test Structure
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
import pytest
|
|
95
|
+
from mdkeychunker.module import Function
|
|
96
|
+
|
|
97
|
+
@pytest.fixture
|
|
98
|
+
def config():
|
|
99
|
+
"""Create test configuration."""
|
|
100
|
+
return Config(param1=value1)
|
|
101
|
+
|
|
102
|
+
def test_basic_functionality(config):
|
|
103
|
+
"""Test basic behavior."""
|
|
104
|
+
result = Function(config)
|
|
105
|
+
assert result == expected
|
|
106
|
+
|
|
107
|
+
def test_edge_case():
|
|
108
|
+
"""Test edge case handling."""
|
|
109
|
+
with pytest.raises(ValueError):
|
|
110
|
+
Function(invalid_input)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Running Tests
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
# All tests
|
|
117
|
+
pytest
|
|
118
|
+
|
|
119
|
+
# Specific file
|
|
120
|
+
pytest tests/test_chunker.py
|
|
121
|
+
|
|
122
|
+
# With coverage
|
|
123
|
+
pytest --cov=mdkeychunker --cov-report=html
|
|
124
|
+
|
|
125
|
+
# Verbose output
|
|
126
|
+
pytest -v
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Pull Request Process
|
|
130
|
+
|
|
131
|
+
1. **Create a feature branch**
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
git checkout -b feature/your-feature-name
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
2. **Make your changes**
|
|
138
|
+
- Write code following style guidelines
|
|
139
|
+
- Add tests for new functionality
|
|
140
|
+
- Update documentation as needed
|
|
141
|
+
|
|
142
|
+
3. **Verify tests pass**
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pytest
|
|
146
|
+
mypy mdkeychunker/
|
|
147
|
+
ruff format --check mdkeychunker/
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
4. **Commit with clear messages**
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
git commit -m "Add feature: description of what was added"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
5. **Push and create PR**
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
git push origin feature/your-feature-name
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
Then create a pull request on GitHub with:
|
|
163
|
+
|
|
164
|
+
- Clear title describing the change
|
|
165
|
+
- Description of what was changed and why
|
|
166
|
+
- Link to any related issues
|
|
167
|
+
- Screenshots/examples if applicable
|
|
168
|
+
|
|
169
|
+
## Areas for Contribution
|
|
170
|
+
|
|
171
|
+
### High Priority
|
|
172
|
+
|
|
173
|
+
- **Performance optimization**: Profile and optimize bottlenecks for large documents
|
|
174
|
+
- **Additional LLM providers**: Add support for Cohere, Mistral, etc.
|
|
175
|
+
- **Documentation**: Improve examples, tutorials, API docs
|
|
176
|
+
- **Streaming support**: Process very large documents chunk-by-chunk
|
|
177
|
+
|
|
178
|
+
### Medium Priority
|
|
179
|
+
|
|
180
|
+
- **Adaptive chunking**: Dynamic chunk sizing based on content
|
|
181
|
+
- **Streaming API**: Process large documents in chunks
|
|
182
|
+
- **Export formats**: Neo4j graph export, custom formats
|
|
183
|
+
- **Visualization**: Generate chunk relationship graphs
|
|
184
|
+
|
|
185
|
+
### Nice to Have
|
|
186
|
+
|
|
187
|
+
- **Web UI**: Simple interface for testing
|
|
188
|
+
- **Benchmarking suite**: Standardized performance tests
|
|
189
|
+
- **Integration examples**: Sample RAG pipeline implementations
|
|
190
|
+
- **Docker support**: Containerized deployment
|
|
191
|
+
|
|
192
|
+
## Documentation
|
|
193
|
+
|
|
194
|
+
When adding new features:
|
|
195
|
+
|
|
196
|
+
1. **Update README.md** if user-facing
|
|
197
|
+
2. **Add docstrings** to all functions/classes
|
|
198
|
+
3. **Update .env.sample** for new config options
|
|
199
|
+
4. **Add examples** in `examples.py` if applicable
|
|
200
|
+
|
|
201
|
+
## Code Review Process
|
|
202
|
+
|
|
203
|
+
All submissions require review. We look for:
|
|
204
|
+
|
|
205
|
+
- **Functionality**: Does it work as intended?
|
|
206
|
+
- **Tests**: Are there adequate tests?
|
|
207
|
+
- **Code quality**: Is it readable and maintainable?
|
|
208
|
+
- **Documentation**: Is it well-documented?
|
|
209
|
+
- **Performance**: Any performance implications?
|
|
210
|
+
|
|
211
|
+
## Reporting Bugs
|
|
212
|
+
|
|
213
|
+
Use GitHub Issues with:
|
|
214
|
+
|
|
215
|
+
- **Clear title**: Concise description of the bug
|
|
216
|
+
- **Environment**: OS, Python version, package version
|
|
217
|
+
- **Reproduction steps**: Minimal code to reproduce
|
|
218
|
+
- **Expected vs. actual behavior**
|
|
219
|
+
- **Logs/errors**: Full error messages
|
|
220
|
+
|
|
221
|
+
## Feature Requests
|
|
222
|
+
|
|
223
|
+
Use GitHub Issues with:
|
|
224
|
+
|
|
225
|
+
- **Use case**: Why is this feature needed?
|
|
226
|
+
- **Proposed solution**: How should it work?
|
|
227
|
+
- **Alternatives**: Other approaches considered
|
|
228
|
+
- **Impact**: Who would benefit?
|
|
229
|
+
|
|
230
|
+
## Questions?
|
|
231
|
+
|
|
232
|
+
- Open a GitHub Discussion for general questions
|
|
233
|
+
- Check existing issues/PRs for similar topics
|
|
234
|
+
- Tag maintainers for urgent items
|
|
235
|
+
|
|
236
|
+
## License
|
|
237
|
+
|
|
238
|
+
By contributing, you agree that your contributions will be licensed under the MIT License.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Bhavik Mangla
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Manifest file for package distribution
|
|
2
|
+
|
|
3
|
+
include README.md
|
|
4
|
+
include LICENSE
|
|
5
|
+
include CONTRIBUTING.md
|
|
6
|
+
include .env.sample
|
|
7
|
+
include demo.md
|
|
8
|
+
include examples.py
|
|
9
|
+
|
|
10
|
+
recursive-include mdkeychunker *.py
|
|
11
|
+
recursive-include tests *.py
|
|
12
|
+
|
|
13
|
+
exclude .gitignore
|
|
14
|
+
exclude setup.sh
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mdkeychunker
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Markdown chunking with LLM enrichment for RAG pipelines
|
|
5
|
+
Author: Bhavik Mangla
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/bhavik-mangla/MDKeyChunker
|
|
8
|
+
Project-URL: Repository, https://github.com/bhavik-mangla/MDKeyChunker
|
|
9
|
+
Project-URL: Issues, https://github.com/bhavik-mangla/MDKeyChunker/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/bhavik-mangla/MDKeyChunker/releases
|
|
11
|
+
Keywords: markdown,chunking,rag,llm,retrieval-augmented-generation
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: openai>=1.0.0
|
|
24
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
25
|
+
Requires-Dist: tiktoken>=0.5.0
|
|
26
|
+
Provides-Extra: anthropic
|
|
27
|
+
Requires-Dist: anthropic>=0.18.0; extra == "anthropic"
|
|
28
|
+
Provides-Extra: spacy
|
|
29
|
+
Requires-Dist: spacy>=3.0.0; extra == "spacy"
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest>=7.4.0; extra == "dev"
|
|
32
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
33
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
34
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
35
|
+
Provides-Extra: benchmark
|
|
36
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "benchmark"
|
|
37
|
+
Requires-Dist: datasets>=2.14.0; extra == "benchmark"
|
|
38
|
+
Requires-Dist: beir>=2.0.0; extra == "benchmark"
|
|
39
|
+
Requires-Dist: torch>=2.0.0; extra == "benchmark"
|
|
40
|
+
Requires-Dist: tqdm>=4.66.0; extra == "benchmark"
|
|
41
|
+
Requires-Dist: matplotlib>=3.7.0; extra == "benchmark"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# MDKeyChunker
|
|
45
|
+
|
|
46
|
+
[](https://www.python.org/downloads/)
|
|
47
|
+
[](https://opensource.org/licenses/MIT)
|
|
48
|
+
[](https://arxiv.org/abs/2603.23533)
|
|
49
|
+
|
|
50
|
+
Markdown chunking with single-call LLM enrichment or ultra-fast spaCy processing for RAG pipelines.
|
|
51
|
+
Paper: **[MDKeyChunker: Single-Call LLM Enrichment with Rolling Keys and Key-Based Restructuring for High-Accuracy RAG](https://arxiv.org/abs/2603.23533)** (arXiv:2603.23533)
|
|
52
|
+
|
|
53
|
+
## What It Does
|
|
54
|
+
|
|
55
|
+
1. **Chunks** Markdown into structural units (headers, code blocks, tables, lists)
|
|
56
|
+
2. **Enriches** each chunk with ONE call → title, summary, keywords, entities, questions, semantic key
|
|
57
|
+
- **LLM Mode**: High-fidelity metadata using GPT/Claude/Ollama
|
|
58
|
+
- **spaCy Mode**: Fast, local, and free semantic key extraction
|
|
59
|
+
3. **Passes rolling keys** forward so the enricher has context about prior topics
|
|
60
|
+
4. **Restructures** by merging chunks that share the same specific-subtopic key
|
|
61
|
+
|
|
62
|
+
## Quick Start
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
git clone https://github.com/bhavik-mangla/MDKeyChunker.git
|
|
66
|
+
cd MDKeyChunker
|
|
67
|
+
pip install -e . # add [anthropic] or [spacy] extras as needed
|
|
68
|
+
cp .env.sample .env # then edit .env with your API key
|
|
69
|
+
mdkeychunker demo.md
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
spaCy mode needs the extra and a model: `pip install -e ".[spacy]" && python -m spacy download en_core_web_md`.
|
|
73
|
+
|
|
74
|
+
Or programmatically:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from mdkeychunker import Pipeline, Config
|
|
78
|
+
|
|
79
|
+
# High-fidelity mode (GPT-4o-mini)
|
|
80
|
+
config = Config.from_env()
|
|
81
|
+
pipeline = Pipeline(config, enricher_mode="llm")
|
|
82
|
+
chunks = pipeline.process_file("document.md")
|
|
83
|
+
|
|
84
|
+
# Ultra-fast / Free mode (spaCy)
|
|
85
|
+
pipeline_fast = Pipeline(config, enricher_mode="spacy")
|
|
86
|
+
chunks_fast = pipeline_fast.process_file("document.md")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## CLI
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# Basic usage
|
|
93
|
+
mdkeychunker document.md
|
|
94
|
+
|
|
95
|
+
# Save to specific output
|
|
96
|
+
mdkeychunker document.md -o chunks.jsonl
|
|
97
|
+
|
|
98
|
+
# With summary file and stats
|
|
99
|
+
mdkeychunker document.md --summary summary.txt --stats
|
|
100
|
+
|
|
101
|
+
# Disable merging
|
|
102
|
+
mdkeychunker document.md --no-merge
|
|
103
|
+
|
|
104
|
+
# Override LLM provider (e.g., local Ollama)
|
|
105
|
+
mdkeychunker document.md --provider openai_compatible --base-url http://localhost:11434/v1 --model llama3
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Configuration
|
|
109
|
+
|
|
110
|
+
Set via `.env` file or environment variables:
|
|
111
|
+
|
|
112
|
+
| Variable | Default | Description |
|
|
113
|
+
|---|---|---|
|
|
114
|
+
| `LLM_PROVIDER` | `openai` | `openai` \| `anthropic` \| `openai_compatible` |
|
|
115
|
+
| `LLM_API_KEY` | — | API key (not needed for local Ollama) |
|
|
116
|
+
| `LLM_BASE_URL` | — | Base URL for Ollama/vLLM/LM Studio |
|
|
117
|
+
| `LLM_MODEL` | `gpt-4o-mini` | Model name |
|
|
118
|
+
| `MIN_CHUNK_SIZE` | `100` | Minimum characters per chunk |
|
|
119
|
+
| `MAX_CHUNK_SIZE` | `1500` | Soft max characters per chunk |
|
|
120
|
+
| `MERGE_BY_KEYS` | `true` | Merge chunks sharing the same key |
|
|
121
|
+
| `MAX_MERGED_SIZE` | `3000` | Max combined size after merging |
|
|
122
|
+
| `MIN_ORPHAN_SIZE` | `200` | Below this, orphan chunks get context enrichment |
|
|
123
|
+
| `LOG_LEVEL` | `INFO` | Logging verbosity |
|
|
124
|
+
|
|
125
|
+
## LLM Providers
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# OpenAI
|
|
129
|
+
LLM_PROVIDER=openai
|
|
130
|
+
LLM_API_KEY=sk-...
|
|
131
|
+
LLM_MODEL=gpt-4o-mini
|
|
132
|
+
|
|
133
|
+
# Anthropic
|
|
134
|
+
LLM_PROVIDER=anthropic
|
|
135
|
+
LLM_API_KEY=sk-ant-...
|
|
136
|
+
LLM_MODEL=claude-3-haiku-20240307
|
|
137
|
+
|
|
138
|
+
# Ollama (local)
|
|
139
|
+
LLM_PROVIDER=openai_compatible
|
|
140
|
+
LLM_BASE_URL=http://localhost:11434/v1
|
|
141
|
+
LLM_MODEL=llama3
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## How It Works
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
Markdown → Chunker → Enricher (1 LLM call/chunk) → Restructurer → Enriched Chunks
|
|
148
|
+
↑
|
|
149
|
+
Rolling Keys
|
|
150
|
+
(context from prior chunks)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
**Key design**: The `key` field means the *specific subtopic* that distinguishes a chunk — e.g. `"admissions process"`, `"oauth token flow"`, `"gradient descent optimization"` — never the broad document topic. Chunks with matching keys are merged globally, regardless of position.
|
|
154
|
+
|
|
155
|
+
## Chunk Schema
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"chunk_id": "a3f2b1c4d5e6f7a8",
|
|
160
|
+
"text": "The admissions process begins in March...",
|
|
161
|
+
"section_title": "Admissions",
|
|
162
|
+
"title": "Spring Admissions Timeline",
|
|
163
|
+
"summary": "Describes the March start of the admissions process...",
|
|
164
|
+
"keywords": ["admissions", "application deadline", "March intake"],
|
|
165
|
+
"entities": [{"name": "March", "type": "EVENT"}],
|
|
166
|
+
"questions": ["When does the admissions process begin?"],
|
|
167
|
+
"key": "admissions process",
|
|
168
|
+
"related_keys": ["curriculum framework"],
|
|
169
|
+
"content_types": ["paragraph"],
|
|
170
|
+
"position_index": 2,
|
|
171
|
+
"previous_chunk_id": "9b8c7d6e5f4a3b2c",
|
|
172
|
+
"next_chunk_id": "1a2b3c4d5e6f7a8b",
|
|
173
|
+
"token_count": 187,
|
|
174
|
+
"start_line": 12,
|
|
175
|
+
"end_line": 28
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## Benchmarks
|
|
180
|
+
|
|
181
|
+
The paper's evaluation (30 queries over an 18-document Markdown corpus, Configs A–D) is described in [arXiv:2603.23533](https://arxiv.org/abs/2603.23533). The code version used in the paper is commit `e3e1b86`.
|
|
182
|
+
|
|
183
|
+
`benchmarks/scifact.py` runs a SciFact sanity check in spaCy mode (`pip install -e ".[benchmark,spacy]"`, then `python benchmarks/scifact.py`). Embeddings are [BAAI/bge-small-en-v1.5](https://huggingface.co/BAAI/bge-small-en-v1.5) with exact cosine search.
|
|
184
|
+
|
|
185
|
+

|
|
186
|
+
|
|
187
|
+
| Strategy | Library | Recall@5 | nDCG@5 | Chunks/Doc |
|
|
188
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
189
|
+
| MDKeyChunker (spaCy mode) | — | 0.762 | 0.681 | 1.00 |
|
|
190
|
+
| [Recursive Character](https://github.com/langchain-ai/langchain) | LangChain | 0.760 | 0.678 | 3.43 |
|
|
191
|
+
| [Semantic Chunker](https://github.com/langchain-ai/langchain-experimental) | LangChain | 0.775 | 0.681 | 2.03 |
|
|
192
|
+
| Fixed Token (512) | Standard | 0.762 | 0.681 | 1.05 |
|
|
193
|
+
|
|
194
|
+
**Read this table with care.** SciFact abstracts are short single-paragraph plain text, so MDKeyChunker emits one chunk per abstract and this run effectively measures whole-abstract retrieval. It does not exercise Markdown structure, LLM enrichment, or key-based restructuring, and it is not evidence for those stages. The baseline rows were produced with scripts that are not yet in this repository.
|
|
195
|
+
|
|
196
|
+
## API
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from mdkeychunker import Pipeline, Config, Chunk
|
|
200
|
+
|
|
201
|
+
# Config
|
|
202
|
+
config = Config(llm_provider="openai", llm_model="gpt-4o-mini", merge_by_keys=True)
|
|
203
|
+
|
|
204
|
+
# Pipeline
|
|
205
|
+
pipeline = Pipeline(config)
|
|
206
|
+
chunks: list[Chunk] = pipeline.process_text(markdown_text)
|
|
207
|
+
chunks: list[Chunk] = pipeline.process_file("path/to/file.md")
|
|
208
|
+
|
|
209
|
+
# Save output
|
|
210
|
+
pipeline.save_jsonl(chunks, "output.jsonl")
|
|
211
|
+
pipeline.save_summary(chunks, "summary.txt")
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Testing
|
|
215
|
+
|
|
216
|
+
```bash
|
|
217
|
+
pip install -e ".[dev]"
|
|
218
|
+
pytest tests/ -v
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Citation
|
|
222
|
+
|
|
223
|
+
If you use MDKeyChunker, please cite:
|
|
224
|
+
|
|
225
|
+
```bibtex
|
|
226
|
+
@misc{mangla2026mdkeychunker,
|
|
227
|
+
title = {{MDKeyChunker}: Single-Call {LLM} Enrichment with Rolling Keys and Key-Based Restructuring for High-Accuracy {RAG}},
|
|
228
|
+
author = {Mangla, Bhavik},
|
|
229
|
+
year = {2026},
|
|
230
|
+
eprint = {2603.23533},
|
|
231
|
+
archivePrefix = {arXiv},
|
|
232
|
+
primaryClass = {cs.CL},
|
|
233
|
+
url = {https://arxiv.org/abs/2603.23533}
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
GitHub's "Cite this repository" button uses [`CITATION.cff`](CITATION.cff).
|
|
238
|
+
|
|
239
|
+
## License
|
|
240
|
+
|
|
241
|
+
MIT
|