Skip to content

feat: DocumentSplitter — add split_by="token" mode using tiktoken - #12529

Open
rautaditya2606 wants to merge 1 commit into
deepset-ai:mainfrom
rautaditya2606:feat/document-splitter-token
Open

feat: DocumentSplitter — add split_by="token" mode using tiktoken#12529
rautaditya2606 wants to merge 1 commit into
deepset-ai:mainfrom
rautaditya2606:feat/document-splitter-token

Conversation

@rautaditya2606

Copy link
Copy Markdown
Contributor

Related Issues

closes #12528

Proposed Changes

DocumentSplitter supports 7 split_by modes but not token-based splitting. Users who need
simple token-aware chunking are forced to use RecursiveDocumentSplitter even when they don't
need recursion. This PR adds split_by="token" to DocumentSplitter using tiktoken, aligning
it with the existing split_unit="token" support in RecursiveDocumentSplitter.

What changed:

  • split_by="token" added to the Literal type and valid_split_by list
  • New tokenizer_encoding: str = "o200k_base" param (keyword-only, matches RecursiveDocumentSplitter default)
  • Lazy tiktoken import via LazyImport — same pattern as RecursiveDocumentSplitter
  • __init__: eager tiktoken_imports.check() if token mode; sets self._tiktoken_tokenizer = None
  • warm_up(): loads tiktoken encoder if token mode — mirrors sentence splitter pattern exactly
  • run(): auto-triggers warm_up() for token mode — same lazy-init pattern as sentence mode
  • _split_by_token(): encode → slice by [i : i + split_length] with step = split_length - split_overlap → decode; tracks cur_page and cur_start_idx; delegates to _create_docs_from_splits
  • to_dict(): serialises tokenizer_encoding

Before:

# No token-aware option — forced to use RecursiveDocumentSplitter
splitter = RecursiveDocumentSplitter(split_length=256, split_overlap=32, split_unit="token")

After:

splitter = DocumentSplitter(split_by="token", split_length=256, split_overlap=32)
result = splitter.run(documents=[Document(content="...")])

How did you test it?

Added TestSplittingByToken class (7 @pytest.mark.integration tests) to
test/components/preprocessors/test_document_splitter.py:

  • test_basic_chunking — chunks produced, no chunk exceeds split_length tokens
  • test_metadata_setsource_id, split_id, split_idx_start, page_number all set correctly
  • test_overlap_produces_shared_text — overlap produces correct index overlap between consecutive chunks
  • test_page_trackingpage_number advances correctly across \f boundaries
  • test_empty_document_skipped — empty content skipped with correct log message
  • test_custom_encodingtokenizer_encoding="cl100k_base" works correctly
  • test_serialization_roundtripto_dict / from_dict round-trip preserves all params

All 56 existing unit tests and 7 new integration tests pass. mypy clean.

hatch run test:unit test/components/preprocessors/test_document_splitter.py
hatch run test:integration test/components/preprocessors/test_document_splitter.py
hatch run test:types

Notes for the reviewer

  • _split_by_token is self-contained and does not share code with RecursiveDocumentSplitter
    the recursive splitter's tokenizer logic is deeply intertwined with its overlap and chunk-length
    helpers. A future refactor can extract shared utilities if needed.
  • split_threshold is not applied in token mode — consistent with how it is silently irrelevant
    for sentence and function modes.
  • The existing split_overlap >= split_length guard at _init_checks L153–154 covers token mode
    automatically — no additional guard needed.

Checklist

  • I have read the contributors guidelines and the code of conduct.
  • I have updated the related issue with new insights and changes.
  • I have added unit tests and updated the docstrings.
  • I've used one of the conventional commit types for my PR title: fix:, feat:, build:, chore:, ci:, docs:, style:, refactor:, perf:, test: and added ! in case the PR includes breaking changes.
  • I have documented my code.
  • I have added a release note file, following the contributors guidelines.
  • I have run pre-commit hooks and fixed any issue.

@rautaditya2606
rautaditya2606 requested a review from a team as a code owner August 31, 2026 10:57
@rautaditya2606
rautaditya2606 requested review from anakin87 and a lite review from Copilot and removed request for a team August 31, 2026 10:57
@vercel

vercel Bot commented Aug 31, 2026

Copy link
Copy Markdown

@rautaditya2606 is attempting to deploy a commit to the deepset Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added topic:tests type:documentation Improvements on the docs labels Aug 31, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  haystack/components/preprocessors
  document_splitter.py 130-131, 187, 210, 236, 272-295
Project Total  

This report was generated by python-coverage-comment-action

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

topic:tests type:documentation Improvements on the docs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: DocumentSplitter — add split_by="token" mode using tiktoken

2 participants