windlass.core.text¶
text
¶
Text utilities shared by loaders, chunkers, retrievers and prompts.
Everything here is dependency-free and deterministic. Token counting uses
tiktoken when it is installed and a calibrated heuristic otherwise, so a
chunker configured in tokens behaves sensibly on a core-only install.
count_tokens
¶
Estimate how many tokens text occupies.
Exact when tiktoken is installed; otherwise a character-ratio estimate
that is close enough for chunk sizing and budget checks.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The text to measure. |
required |
model
|
str
|
Model name used to pick the tokeniser. |
'gpt-4o'
|
Returns:
| Type | Description |
|---|---|
int
|
Token count. Always at least 1 for non-empty text. |
Example
count_tokens("") == 0 True count_tokens("hello world") >= 1 True
Source code in src\windlass\core\text.py
truncate_tokens
¶
Trim text so it fits within max_tokens.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The text to trim. |
required |
max_tokens
|
int
|
Ceiling, inclusive. |
required |
model
|
str
|
Model name used to pick the tokeniser. |
'gpt-4o'
|
suffix
|
str
|
Appended when truncation happened, e.g. |
''
|
Returns:
| Type | Description |
|---|---|
str
|
The original text when it already fits, otherwise a truncated copy. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Example
truncate_tokens("short", 100) 'short'
Source code in src\windlass\core\text.py
normalize_whitespace
¶
Collapse runs of spaces and (optionally) blank lines.
Preserves paragraph structure — two newlines survive, three or more collapse to two — because chunkers rely on paragraph boundaries.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Input text. |
required |
collapse_newlines
|
bool
|
Also collapse runs of 3+ newlines to 2. |
True
|
Returns:
| Type | Description |
|---|---|
str
|
Cleaned text with trailing whitespace stripped from each line. |
Example
normalize_whitespace("a \t b\n\n\n\nc") 'a b\n\nc'
Source code in src\windlass\core\text.py
normalize_unicode
¶
normalize_unicode(
text: str,
*,
form: Literal["NFC", "NFD", "NFKC", "NFKD"] = "NFKC",
strip_control: bool = True
) -> str
Apply Unicode normalisation and drop control characters.
PDF extraction routinely emits ligatures, non-breaking spaces and stray control codes; leaving them in produces chunk boundaries and embeddings that do not match what a user would type.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Input text. |
required |
form
|
Literal['NFC', 'NFD', 'NFKC', 'NFKD']
|
Normalisation form ( |
'NFKC'
|
strip_control
|
bool
|
Remove C0/C1 control characters except tab and newline. |
True
|
Returns:
| Type | Description |
|---|---|
str
|
Normalised text. |
Example
normalize_unicode("file") 'file'
Source code in src\windlass\core\text.py
strip_html
¶
Remove tags from an HTML fragment, keeping the readable text.
A dependency-free fallback. The html loader prefers BeautifulSoup, which
handles malformed markup far better; this exists so that HTML embedded in
other formats can still be cleaned on a core-only install.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
html
|
str
|
HTML source. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Plain text with entities resolved and whitespace normalised. |
Example
strip_html("
Hello world
") 'Hello world'
Source code in src\windlass\core\text.py
split_sentences
¶
Split text into sentences.
Tuned to avoid the classic false positives: single-letter initials
(J. Smith), common abbreviations (Dr., etc., Inc.) and
decimals ($1.50). Good enough for semantic chunking without dragging in
an NLP toolkit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Input text. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
Non-empty, stripped sentences in reading order. |
Example
split_sentences("Dr. Smith paid $1.50. Then he left.") ['Dr. Smith paid $1.50.', 'Then he left.'] split_sentences("A. B. Jones arrived. He waited.") ['A. B. Jones arrived.', 'He waited.']
Source code in src\windlass\core\text.py
split_paragraphs
¶
Split text on blank lines.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Input text. |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
Non-empty, stripped paragraphs. |
Example
split_paragraphs("one\n\ntwo") ['one', 'two']
Source code in src\windlass\core\text.py
tokenize_words
¶
Split text into word tokens for lexical search.
Used by the BM25 retriever. Unicode-aware, keeps intra-word apostrophes and
hyphens so state-of-the-art and don't survive intact.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Input text. |
required |
lowercase
|
bool
|
Case-fold the output. |
True
|
Returns:
| Type | Description |
|---|---|
list[str]
|
Word tokens in document order. |
Example
tokenize_words("State-of-the-art RAG, don't you think?") ['state-of-the-art', 'rag', "don't", 'you', 'think']
Source code in src\windlass\core\text.py
sliding_window
¶
Yield overlapping windows over a list.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
items
|
list[Any]
|
The sequence to window. |
required |
size
|
int
|
Window length. |
required |
overlap
|
int
|
How many items each window shares with the previous one. |
0
|
Yields:
| Type | Description |
|---|---|
list[Any]
|
Windows of at most |
Raises:
| Type | Description |
|---|---|
ValueError
|
If |
Example
list(sliding_window([1, 2, 3, 4], size=2, overlap=1)) [[1, 2], [2, 3], [3, 4]]
Source code in src\windlass\core\text.py
detect_language
¶
Guess the ISO 639-1 language code of text.
Prefers langdetect when installed. The fallback uses stop-word overlap
plus script detection (CJK, Cyrillic, Arabic, Devanagari), which is accurate
enough to route documents or tag metadata but is not a replacement for a
real classifier.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Input text; the first 2000 characters are inspected. |
required |
default
|
str
|
Returned when detection is inconclusive. |
'en'
|
Returns:
| Type | Description |
|---|---|
str
|
A two-letter language code. |
Example
detect_language("the quick brown fox is in the garden") 'en' detect_language("") 'en'
Source code in src\windlass\core\text.py
text_hash
¶
Return a stable hash of text for deduplication.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Input text. |
required |
normalize
|
bool
|
Case-fold and collapse whitespace first, so cosmetic differences do not defeat deduplication. |
True
|
Returns:
| Type | Description |
|---|---|
str
|
A 32-character hex digest. |
Example
text_hash("Hello World") == text_hash("hello world") True
Source code in src\windlass\core\text.py
join_nonempty
¶
Join the truthy, stripped members of parts.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parts
|
Iterable[str]
|
Candidate strings. |
required |
separator
|
str
|
Separator inserted between kept parts. |
'\n\n'
|
Returns:
| Type | Description |
|---|---|
str
|
The joined string. |
Example
join_nonempty(["a", "", " ", "b"], "-") 'a-b'