Classify text fields by structural type. Text is categorized as prose, code, or structured — useful for filtering large JSONL exports before downstream processing (translation, indexing, analysis, etc.).
Available as a Rust CLI, a Rust library crate, and a pip-installable Python package.
Two-tier hybrid classification with an embedded ONNX model:
- Model-first — When compiled with
--features onnx-model, a 41-feature neural network classifies all inputs. The model outputs a probability distribution over 31 sub-types (e.g.python,markdown,csv), marginalized into 3 categories. - Tier 1 fallback — Without the model, rule-based structural short-circuits handle high-confidence cases (pure prose, tabular data, markup).
| Category | Description | Example sub-types |
|---|---|---|
prose |
Human-readable natural language | plain, markdown, rst, latex |
code |
Source code, scripts, config, markup | python, rust, shell, html, sql |
structured |
Tables, data formats, config files | csv, json, yaml, log_lines, key_value |
The model also produces a sub-type probability distribution — useful as a ranking tensor in search systems like Vespa.
cargo install --path .
# or with the embedded ONNX model:
cargo install --path . --features onnx-model# uv (recommended)
uv add git+https://github.com/SentioLabs/text-classifier-rs --tag v0.3.0
# pip
pip install git+https://github.com/SentioLabs/text-classifier-rs@v0.3.0
# or build from source:
make python-setup && make python-buildNote: Installing from git requires a Rust toolchain — maturin compiles the extension locally. Pin to a release tag for reproducible builds.
[dependencies]
text-classifier = { git = "https://github.com/SentioLabs/text-classifier-rs" }# Classify text from stdin
echo "This is a paragraph of English prose." | classify
# Classify a JSONL file (supports .gz)
classify file input.jsonl -o classified.jsonl
# Filter sub-type scores to significant entries only
classify --min-score 0.01 file input.jsonl -o classified.jsonl
# Filter by category
classify filter export.jsonl.gz \
--prose prose.jsonl \
--skipped non_prose.jsonl \
--text-fields bodytext,summarytext,title
# Dump raw feature vectors as CSV (all 41 features)
classify features input.jsonl -o features.csv
# Generate training labels from Tier 1
classify label-corpus input.jsonl -o labeled.jsonlOutput from classify file includes the full classification and sub-type scores:
{
"_classification": {
"category": "code",
"sub_type": "python",
"confidence": 0.97,
"tier": "model",
"sub_type_scores": {
"code": [{"sub_type": "python", "score": 0.89}, {"sub_type": "shell", "score": 0.05}],
"prose": [{"sub_type": "plain", "score": 0.03}],
"structured": [{"sub_type": "ini", "score": 0.01}]
}
}
}Use --min-score 0.01 to drop noise entries (31 sub-types → 2-5 significant ones).
from text_classifier import Classifier
clf = Classifier()
result = clf.classify("Some text to classify")
print(f"{result.category} (confidence={result.confidence:.2f})")
# With score filtering for production use
clf = Classifier(min_score=0.01)
result = clf.classify("import sys; print(sys.argv)")
print(result.sub_type_scores) # JSON string with significant sub-types onlyuse text_classifier::{Classifier, TextCategory};
// Model-first classification (requires --features onnx-model)
let clf = Classifier::with_embedded_model()
.min_score(0.01); // only keep scores >= 1%
let result = clf.classify("Hello world, this is a test paragraph.");
assert_eq!(result.category, TextCategory::Prose);
// Sub-type probability distribution available on result.sub_type_scores
// Detection head scores available on result.detections
// Batch classification (parallel via rayon)
let results = clf.classify_batch(&["text one", "text two"]);make help # show all available commands
make build # debug build
make test # run all tests
make lint # format check + clippy
make check # lint + test (CI equivalent)
make review # full review with pass/fail summarySee CONTRIBUTING.md for the full contributor guide.
text → Classifier::classify()
├─ empty? → prose, confidence 0.0
├─ too short (< 5 words)? → prose, confidence 0.5
├─ extract_features() → 41 structural signals
├─ ONNX model loaded?
│ └─ yes → model inference → category + sub-type + scores
└─ no → classify_tier1() → rule-based fallback
The feature vector includes signals for line structure, character composition, syntax patterns, and content type indicators. Key discriminators:
| Feature | What it measures | Key discriminator |
|---|---|---|
line_length_cv |
Coefficient of variation of line lengths | Prose (high) vs tables (low) |
char_entropy |
Shannon entropy of character distribution | Natural language (~4.0) vs noise (>5.0) |
symbol_ratio |
Non-alpha, excluding common punct | Code (>0.15) vs prose (<0.05) |
sentence_punctuation_rate |
Sentence-ending punct per word | Prose (~0.04-0.08) vs code (~0) |
json_lines_ratio |
Lines matching {...} pattern |
JSONL detection |
shebang_present |
Text starts with #! |
Shell/script detection |
dictionary_word_ratio |
Known English words fraction | Prose vs code/structured |
inline_markup_count |
Bold, italic, code, link patterns | Markdown detection |
See src/features.rs for the full list of 41 features.