Skip to content

fix(core): fit chunk sizes to the embedding model's sequence limit - #434

Merged
yichuan-w merged 2 commits into
mainfrom
fix/model-aware-chunking
Oct 12, 2026
Merged

yichuan-w merged 2 commits into
mainfrom
fix/model-aware-chunking

Conversation

@yichuan-w

Copy link
Copy Markdown
Collaborator

Problem

leann build used fixed chunk sizes (doc 256, code 512 tiktoken tokens) regardless of the embedding model. The macOS default all-MiniLM-L6-v2 reads only 256 tokens, so on a real leann build of leann-core sources 43% of the indexed code text was never embedded (median code chunk = 461 MiniLM tokens).

Two related bugs:

  • get_model_token_limit() didn't know Hugging Face sentence-transformers models and fell back to 2048.
  • create_text_chunks(max_tokens_per_chunk=...) replaced chunk_size instead of capping it, so an explicit 256 grew to ~1700 for those models.

Fix

  • cli: cap --doc-chunk-size / --code-chunk-size at 70% of the model's limit (overlap capped to half). 70% because WordPiece counts ~30% more tokens than tiktoken on web text/code, and SentenceSplitter overshoots on spans it can't split (tables, long lines). Prints 📏 … chunk size doc 256 → 179, code 512 → 179 when it applies. Models with large limits (OpenAI, nomic, Qwen3) are unaffected.
  • embedding_compute: read max_seq_length from the repo's sentence_bert_config.json for local HF models (only when no base_url, name contains /).
  • chunking_utils: max_tokens_per_chunk only shrinks chunk_size (traditional and AST paths).

Results (all-MiniLM-L6-v2)

Real leann build + LeannSearcher on leann-core sources, 18 natural-language code queries (relevant = chunk containing the target def):

chunks text not embedded hit@1 hit@5 MRR@10
before (512) 402 43.3% 0.11 0.56 0.301
after (179) 1941 0.0% 0.44 0.56 0.535

Offline sweep (brute-force cosine): code MRR 0.258 @512 → 0.607 @256 → 0.658 @179. On 42 web pages / 22 queries, doc sizes 179–256 are within noise (MRR 0.62–0.73), so the doc cap is about guaranteeing text gets embedded rather than a quality jump.

Trade-off: more, smaller chunks for small models (≈4.8× on code), so builds embed more passages.

Tests

  • New tests/test_chunk_size_cap.py (cap only shrinks; large limit doesn't grow chunks; HF discovery and its failure path, no Hub call with base_url or bare names).
  • CI=true pytest tests/: 463 passed; 7 failures are pre-existing on main locally (diskann not installed, test_cli_prompt_template sync pickle).

Existing indexes are untouched; leann build --force re-chunks.

🤖 Generated with Claude Code

yichuan-w and others added 2 commits October 12, 2026 13:42
leann build used fixed 256/512-token chunks regardless of the model. The macOS
default all-MiniLM-L6-v2 reads only 256 tokens, so ~43% of indexed code text was
never embedded.

- cli: cap doc/code chunk size at 70% of the model's limit (WordPiece counts
  ~30% above tiktoken and SentenceSplitter overshoots on unsplittable spans)
- embedding_compute: read max_seq_length from Hugging Face sentence-transformers
  repos instead of defaulting them to 2048
- chunking_utils: max_tokens_per_chunk only shrinks chunk_size (it used to
  replace it, growing 256 to ~1700 for unknown models)

Code retrieval on leann-core sources (18 queries, real indexes): MRR@10
0.30 -> 0.54, hit@1 0.11 -> 0.44.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first SentenceSplitter use in CI loads NLTK stopwords, which the runner's
path guard can refuse (hard-linked file), so a size comparison depended on test
order. Record the chunk_size/overlap passed to the splitter instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yichuan-w
yichuan-w merged commit 00710ed into main Oct 12, 2026
35 checks passed
@yichuan-w
yichuan-w deleted the fix/model-aware-chunking branch October 12, 2026 21:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant