Skip to content

Commit 2a38ee2

Browse files
committed
feat(chunker): add list-aware strategy and adaptive overlap sizing
- Implement list-aware chunking strategy for list-heavy markdown documents - Preserve nested list hierarchies and bind introduction paragraphs to lists - Add configuration thresholds for list ratio and list count activation - Introduce adaptive overlap context sizing scaling with chunk size (up to 35%) - Replace fixed overlap size with min of configured size and 35% chunk size - Update parser to detect lists, compute list metrics and hierarchy depth - Add comprehensive tests and demo for adaptive overlap behavior - Update documentation and examples to reflect new strategy and overlap logic - Increment plugin version to 2.1.0 with changelog and metadata updates
1 parent 6241752 commit 2a38ee2

51 files changed

Lines changed: 4111 additions & 733 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.qoder/repowiki/en/content/Reference/Chunk Metadata Reference.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ When a chunk contains multiple headers, the `sub_headers` field lists additional
9494
```python
9595
# Example chunk with multiple headers
9696
chunk.metadata = {
97-
"header_path": "/Grades/SDE 12/Impact (Delivery)",
97+
"header_path": "/Grades/DEV-4/Impact (Delivery)",
9898
"sub_headers": ["Complexity", "Leadership"], # Additional ### headers
9999
...
100100
}
@@ -104,7 +104,7 @@ The `sub_headers` field contains headers that are children of the root section (
104104
- Headers with level greater than the contextual level (deeper than root section)
105105
- Headers with the same level as the contextual level but different text (siblings of root, not root itself)
106106

107-
This design ensures that all chunks within a section (e.g., SDE 12) have the same `header_path`, while still capturing the local section structure within the chunk.
107+
This design ensures that all chunks within a section (e.g., DEV-4) have the same `header_path`, while still capturing the local section structure within the chunk.
108108

109109
**Section sources**
110110
- [types.py](file://markdown_chunker_v2/types.py#L119-L127)
@@ -302,15 +302,15 @@ These properties ensure that the metadata system behaves correctly across a wide
302302
Specific examples are tested to verify correct behavior:
303303

304304
```python
305-
class TestSDE12ImpactCase:
305+
class TestDEV4ImpactCase:
306306
"""
307307
Test that chunk with Impact/Complexity/Leadership has header_path
308308
ending with "Impact (Delivery)", not "Complexity".
309309
"""
310310

311311
class TestPreambleSeparation:
312312
"""
313-
Test that links before # Критерии грейдов SDE are in separate preamble chunk.
313+
Test that links before # Критерии грейдов DEV are in separate preamble chunk.
314314
"""
315315
```
316316

.qoder/repowiki/en/content/Testing and Quality/Performance Benchmarks/Performance Benchmarks.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ Benchmark results are stored in a structured JSON format that captures comprehen
339339

340340
```json
341341
{
342-
"timestamp": "2024-12-03T10:30:00.000Z",
342+
"timestamp": "2025-12-03T10:30:00.000Z",
343343
"version": "1.4.0",
344344
"by_size": [
345345
{

CHANGELOG.md

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,49 @@
22

33
All notable changes to the Advanced Markdown Chunker plugin will be documented in this file.
44

5-
## [2.0.2-a0] - 2024-12-05
5+
## [2.1.0] - 2025-12-07
6+
7+
### Added
8+
- **List-Aware Strategy** — Intelligent chunking for list-heavy documents
9+
- Preserves nested list hierarchies (never splits across depth levels)
10+
- Automatically binds introduction paragraphs to their lists
11+
- Smart grouping of related list items based on structure
12+
- Handles bullet lists, numbered lists, and checkboxes
13+
- Activation logic: `list_ratio > 0.40 AND list_count >= 5` (for structured docs) or `list_ratio > 0.40 OR list_count >= 5` (for plain lists)
14+
- Perfect for changelogs, feature lists, task lists, and outlines
15+
- **Competitive advantage:** Unique capability not found in LangChain, LlamaIndex, Unstructured, or Chonkie
16+
17+
- **Adaptive Overlap Sizing** — Context window scales with chunk size
18+
- Replaced fixed `MAX_OVERLAP_CONTEXT_SIZE = 500` with adaptive ratio-based sizing
19+
- Formula: `min(config.overlap_size, chunk_size * 0.35)`
20+
- Small chunks respect configured `overlap_size`
21+
- Large chunks allow up to 35% overlap (e.g., 8KB chunk = up to 2.8KB overlap)
22+
- Prevents metadata bloat while providing sufficient context
23+
- Automatic scaling without manual tuning
24+
25+
### Changed
26+
- **Configuration**: Added 2 new parameters for list-aware strategy
27+
- `list_ratio_threshold=0.40` — minimum ratio of list content to activate strategy
28+
- `list_count_threshold=5` — minimum number of list items to activate strategy
29+
- **Architecture**: Total strategies increased from 3 to 4 (code_aware, structural, list_aware, fallback)
30+
- **Documentation**: Comprehensive README update highlighting competitive advantages
31+
- List-aware strategy prominently featured as unique capability
32+
- Practical examples for changelog processing
33+
- Updated architecture and configuration sections
34+
35+
### Fixed
36+
- PEP 8 compliance: Fixed E203 linter errors (whitespace before ':' in slice notation)
37+
- Code quality: All quality checks pass with 0 errors
38+
39+
### Testing
40+
- Added comprehensive tests for adaptive overlap behavior
41+
- Test for large chunks (validates scaling works)
42+
- Test for small chunks (validates config limit still applies)
43+
- Test for proportional scaling comparison
44+
- Created demonstration script `demo_adaptive_overlap.py`
45+
- All 24 tests in `test_preamble_scenarios.py` pass
46+
47+
## [2.0.2-a0] - 2025-12-05
648

749
### Major Redesign
850
This release is a complete architectural redesign focused on simplification and reliability.
@@ -30,7 +72,7 @@ See [docs/MIGRATION.md](docs/MIGRATION.md) for migration guide from v1.x.
3072

3173
---
3274

33-
## [2.0.0-a3] - 2024-12-03
75+
## [2.0.0-a3] - 2025-12-03
3476

3577
### Added
3678
- Regression and duplication validation to improve chunking reliability

PRIVACY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Privacy Policy for Advanced Markdown Chunker
22

3-
**Last Updated:** December 2024
3+
**Last Updated:** December 2025
44
**Version:** 1.0.0
55

66
## Overview

README.md

Lines changed: 134 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
**Intelligent Markdown document chunking for RAG systems with structural awareness**
66

7-
[![Version](https://img.shields.io/badge/version-2.0.0--a3-orange.svg)](CHANGELOG.md)
7+
[![Version](https://img.shields.io/badge/version-2.1.0-orange.svg)](CHANGELOG.md)
88
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
99
[![Python 3.12+](https://img.shields.io/badge/python-3.12+-blue.svg)](https://www.python.org/downloads/)
1010
[![Dify Plugin](https://img.shields.io/badge/dify-1.9.0+-green.svg)](https://dify.ai/)
@@ -51,17 +51,19 @@ This plugin is designed primarily for **RAG (Retrieval-Augmented Generation)** w
5151
| Breaks code blocks mid-function | Preserves code blocks as atomic units |
5252
| Loses header context | Maintains hierarchical section structure |
5353
| Splits tables and lists | Keeps tables and lists intact |
54-
| One-size-fits-all approach | 6 adaptive strategies based on content |
54+
| One-size-fits-all approach | 4 adaptive strategies based on content |
5555
| No overlap support | Smart overlap for better retrieval |
56+
| **Destroys list hierarchies** | **Smart list grouping with context binding** |
5657

5758
---
5859

5960
## ✨ Features
6061

6162
### 🎯 Adaptive Chunking
62-
- **3 intelligent strategies** — automatic selection based on content analysis
63+
- **4 intelligent strategies** — automatic selection based on content analysis
64+
- **List-Aware Strategy** — preserves nested list hierarchies and context (unique competitive advantage)
6365
- **Structure preservation** — headers, lists, tables, and code stay intact
64-
- **Smart overlap**configurable context overlap between chunks
66+
- **Adaptive overlap** — context window scales with chunk size (up to 35%)
6567

6668
### 🔍 Deep Content Analysis
6769
- **AST parsing** — full Markdown syntax analysis
@@ -160,8 +162,8 @@ Add the chunker to your Dify workflow:
160162
|-----------|------|---------|-------------|
161163
| `input_text` | string | required | Markdown text to chunk |
162164
| `max_chunk_size` | number | 4096 | Maximum chunk size in characters |
163-
| `chunk_overlap` | number | 200 | Overlap between consecutive chunks (see below) |
164-
| `strategy` | select | auto | Chunking strategy (auto/code_aware/structural/fallback) |
165+
| `chunk_overlap` | number | 200 | Base overlap size (adaptive: actual max = min(overlap_size, chunk_size * 0.35)) |
166+
| `strategy` | select | auto | Chunking strategy (auto/code_aware/list_aware/structural/fallback) |
165167
| `include_metadata` | boolean | true | Embed metadata in chunk text (see below) |
166168

167169
### Understanding `chunk_overlap`
@@ -306,10 +308,47 @@ chunker = MarkdownChunker()
306308
chunks = chunker.chunk(text)
307309

308310
# Force specific strategy
309-
chunks = chunker.chunk(text, strategy="code")
311+
chunks = chunker.chunk(text, strategy="code_aware")
312+
chunks = chunker.chunk(text, strategy="list_aware") # For list-heavy docs
310313
chunks = chunker.chunk(text, strategy="structural")
311314
```
312315

316+
### List-Aware Strategy Example
317+
318+
```python
319+
from markdown_chunker import MarkdownChunker, ChunkConfig
320+
321+
# Changelog processing with list-aware strategy
322+
changelog = """
323+
# Changelog
324+
325+
## Version 2.0
326+
327+
New features:
328+
- **Authentication**
329+
- OAuth 2.0 support
330+
- SAML integration
331+
- MFA with SMS and authenticator apps
332+
- **Performance**
333+
- 50% faster processing
334+
- Reduced memory usage
335+
"""
336+
337+
config = ChunkConfig(
338+
max_chunk_size=2000,
339+
list_ratio_threshold=0.35, # Lower threshold for changelogs
340+
list_count_threshold=3 # Activate with fewer lists
341+
)
342+
343+
chunker = MarkdownChunker(config)
344+
chunks = chunker.chunk(changelog)
345+
346+
# Result: Nested items stay together, context preserved
347+
for chunk in chunks:
348+
print(f"Chunk type: {chunk.metadata['content_type']}")
349+
print(f"List depth: {chunk.metadata.get('max_list_depth', 0)}")
350+
```
351+
313352
### Configuration Profiles
314353

315354
```python
@@ -365,8 +404,57 @@ The system automatically selects the optimal strategy based on content analysis:
365404
| Strategy | Priority | Activation Conditions | Best For |
366405
|----------|----------|----------------------|----------|
367406
| **Code-Aware** | 1 (highest) | code_ratio ≥ 30% OR has code blocks/tables | Technical docs, API docs |
368-
| **Structural** | 2 | ≥3 headers | Documentation, guides |
369-
| **Fallback** | 3 (default) | Always applicable | Simple text, mixed content |
407+
| **List-Aware** | 2 | list_ratio > 40% OR list_count ≥ 5 (AND logic for structured docs) | Changelogs, feature lists, task lists, outlines |
408+
| **Structural** | 3 | ≥3 headers with hierarchy | Documentation, guides |
409+
| **Fallback** | 4 (default) | Always applicable | Simple text, mixed content |
410+
411+
### List-Aware Strategy: Competitive Advantage
412+
413+
**Unique capability not found in competing solutions** (LangChain, LlamaIndex, Unstructured, Chonkie):
414+
415+
**Intelligent List Processing:**
416+
- **Hierarchy Preservation** — nested lists never split across depth levels
417+
- **Context Binding** — introduction paragraphs automatically attached to their lists
418+
- **Smart Grouping** — related list items kept together based on structure
419+
- **Type Detection** — handles bullet lists, numbered lists, and checkboxes
420+
421+
**Activation Logic:**
422+
```python
423+
# For documents with strong hierarchical structure (many headers):
424+
activate_if: list_ratio > 0.40 AND list_count >= 5
425+
426+
# For documents without strong structure:
427+
activate_if: list_ratio > 0.40 OR list_count >= 5
428+
```
429+
430+
**Perfect for:**
431+
- **Changelogs** — version releases with nested changes
432+
- **Feature lists** — product capabilities with descriptions
433+
- **Task lists** — todos with sub-tasks and checkboxes
434+
- **Outlines** — structured notes and cheatsheets
435+
436+
**Example Input:**
437+
```markdown
438+
Our product includes:
439+
440+
- **Authentication**
441+
- OAuth 2.0 support
442+
- SAML integration
443+
- MFA options
444+
- SMS
445+
- Authenticator app
446+
- Hardware keys
447+
448+
- **Authorization**
449+
- Role-based access
450+
- Permission groups
451+
```
452+
453+
**Result:** Two coherent chunks preserving full hierarchies:
454+
- Chunk 1: Introduction + Authentication with all nested items
455+
- Chunk 2: Authorization with all nested items
456+
457+
**Competitor Behavior:** Would split nested items, losing context and relationships.
370458

371459
---
372460

@@ -382,19 +470,22 @@ config = ChunkConfig(
382470
max_chunk_size=4096, # Maximum chunk size (chars)
383471
min_chunk_size=512, # Minimum chunk size
384472

385-
# Overlap
386-
overlap_size=200, # Overlap size (0 = disabled)
473+
# Overlap (adaptive sizing)
474+
overlap_size=200, # Base overlap size (0 = disabled)
475+
# Actual max = min(overlap_size, chunk_size * 0.35)
387476

388477
# Behavior
389478
preserve_atomic_blocks=True, # Keep code blocks and tables intact
390479
extract_preamble=True, # Extract content before first header
391480

392481
# Strategy selection thresholds
393-
code_threshold=0.3, # Code ratio for CodeAwareStrategy
394-
structure_threshold=3, # Min headers for StructuralStrategy
482+
code_threshold=0.3, # Code ratio for CodeAwareStrategy
483+
structure_threshold=3, # Min headers for StructuralStrategy
484+
list_ratio_threshold=0.40, # List ratio for ListAwareStrategy
485+
list_count_threshold=5, # Min list blocks for ListAwareStrategy
395486

396487
# Override
397-
strategy_override=None, # Force specific strategy (code_aware/structural/fallback)
488+
strategy_override=None, # Force specific strategy (code_aware/list_aware/structural/fallback)
398489
)
399490
```
400491

@@ -519,10 +610,10 @@ class ChunkingResult:
519610

520611
| Module | Description |
521612
|--------|-------------|
522-
| `markdown_chunker_v2/parser.py` | Markdown parsing and content analysis |
523-
| `markdown_chunker_v2/chunker.py` | Main chunking orchestration |
524-
| `markdown_chunker_v2/strategies/` | 3 chunking strategies (code_aware, structural, fallback) |
525-
| `markdown_chunker_v2/config.py` | Configuration (8 parameters) |
613+
| `markdown_chunker_v2/parser.py` | Markdown parsing and content analysis (with list detection) |
614+
| `markdown_chunker_v2/chunker.py` | Main chunking orchestration (with adaptive overlap) |
615+
| `markdown_chunker_v2/strategies/` | 4 chunking strategies (code_aware, list_aware, structural, fallback) |
616+
| `markdown_chunker_v2/config.py` | Configuration (10 parameters) |
526617
| `markdown_chunker_v2/types.py` | Core data types |
527618
| `provider/` | Dify plugin provider |
528619
| `tools/` | Dify plugin tools |
@@ -532,11 +623,15 @@ class ChunkingResult:
532623
```
533624
dify-markdown-chunker/
534625
├── markdown_chunker_v2/ # Core library (v2.0 redesign)
535-
│ ├── parser.py # Markdown parsing
536-
│ ├── chunker.py # Main chunking logic
537-
│ ├── config.py # Configuration (8 params)
626+
│ ├── parser.py # Markdown parsing (with list detection)
627+
│ ├── chunker.py # Main chunking logic (adaptive overlap)
628+
│ ├── config.py # Configuration (10 params)
538629
│ ├── types.py # Data types
539-
│ └── strategies/ # 3 chunking strategies
630+
│ └── strategies/ # 4 chunking strategies
631+
│ ├── code_aware.py # Code-heavy documents
632+
│ ├── list_aware.py # List-heavy documents
633+
│ ├── structural.py # Header-based structure
634+
│ └── fallback.py # Universal fallback
540635
├── provider/ # Dify plugin provider
541636
├── tools/ # Dify plugin tools
542637
├── tests/ # Test suite (498 tests)
@@ -688,13 +783,26 @@ MIT License — see [LICENSE](LICENSE)
688783

689784
## 📝 Changelog
690785

691-
**Current Version:** 2.0.2-a0 (December 2024)
786+
**Current Version:** 2.0.2-a3 (December 2025)
787+
788+
### v2.0.2-a3 - List-Aware Strategy & Adaptive Overlap
789+
- **NEW: List-Aware Strategy** - intelligent processing for list-heavy documents
790+
- Preserves nested list hierarchies (parent-child relationships)
791+
- Automatic context binding (introduction paragraphs + lists)
792+
- Smart activation (AND logic for structured docs, OR for plain lists)
793+
- Handles bullet lists, numbered lists, and checkboxes
794+
- **Adaptive overlap sizing** - context window scales with chunk size (up to 35%)
795+
- Small chunks: respects configured overlap_size
796+
- Large chunks: allows larger overlap (max 35% of chunk size)
797+
- Formula: `min(overlap_size, chunk_size * 0.35)`
798+
- **Enhanced list detection in parser** - full list analysis with hierarchy depth
799+
- **Comprehensive test coverage** - 24 new tests for list strategy and overlap behavior
692800

693801
### v2.0.2-a0 - Major Redesign
694-
- **Simplified architecture**: 3 strategies instead of 6
695-
- **Simplified configuration**: 8 parameters instead of 32
802+
- **Simplified architecture**: 3 base strategies (code_aware, structural, fallback)
803+
- **Simplified configuration**: 8 core parameters (expanded to 10 in a3)
696804
- **Consolidated types**: Single types.py module
697-
- **Improved test suite**: 445 focused property-based tests
805+
- **Improved test suite**: 498 focused property-based tests
698806
- **Metadata-based overlap**: Context stored in metadata, not merged into content
699807

700808
Full changelog: [CHANGELOG.md](CHANGELOG.md)

0 commit comments

Comments
 (0)