Skip to main content

Semantra

Semantra is an offline, few-shot classification engine for Python. Define a set of classes with example sentences and classify new text without training a model, fine-tuning, hosted APIs, or a vector database.

It combines local semantic embeddings from a bundled open-source ONNX model with pure-Python BM25 lexical matching. The scores are fused at class level and checked with confidence and runner-up margin thresholds, so ambiguous input can return Unknown instead of receiving an unreliable label.

Installation

pip install semantra-classify

The main wheel includes the fast English model and tokenizer assets. To use the multilingual model from PyPI, install the companion asset package too:

pip install semantra-classify semantra-classify-multilingual

Both distributions use the same semantra Python API. After installation, inference does not require internet access, an API key, a model server, or a database. The default English model is distributed under Apache-2.0. A multilingual mode is available for mixed-language and Hinglish input.

Quick start

from semantra import Classifier

classifier = Classifier()
classifier.add_class("account_access", [
    "I cannot sign in to my account",
    "The login page keeps rejecting my password",
    "I am locked out of my profile",
])
classifier.add_class("billing", [
    "I was charged twice for the same order",
    "There is an unexpected charge on my invoice",
    "How can I update my payment details?",
])
classifier.add_class("shipping", [
    "Where is my delivery?",
    "My order has not arrived yet",
    "Can I change the delivery address?",
])

result = classifier.predict("I have been locked out of my profile")
print(result.class_name)   # account_access
print(result.confidence)   # normalized confidence score

Multilingual and Hinglish input

Use the bundled multilingual model when examples and queries may contain Hindi, English, Hinglish, or other supported languages:

from semantra import Classifier

classifier = Classifier(model="multilingual")
classifier.add_class("account_access", [
    "I cannot sign in to my account",
    "Mera account login nahi ho raha",
    "मेरा अकाउंट लॉगिन नहीं हो रहा",
])

result = classifier.predict("Mera account access nahi ho raha")
print(result.class_name, result.confidence)

This mode does not run a language detector, translator, or second classifier: the multilingual embedder handles the normalized input in the same single embedding call, while the Unicode-aware BM25 matcher uses the same text. This keeps the pipeline simple and avoids an extra language-processing latency stage. The multilingual model is larger than the English model, so its model inference can be slower and use more memory; always benchmark on the target CPU using result.inference_time_ms.

The multilingual model is based on XLM-R and is trained for 100 languages. Commonly used supported languages include English, Hindi, Urdu, Bengali, Gujarati, Marathi, Punjabi, Tamil, Telugu, Kannada, Malayalam, Nepali, Arabic, Persian, Hebrew, Turkish, Russian, Ukrainian, Polish, Czech, Slovak, Romanian, Hungarian, Bulgarian, Greek, German, Dutch, Danish, Swedish, Norwegian, Finnish, French, Spanish, Portuguese, Italian, Catalan, Indonesian, Malay, Vietnamese, Thai, Chinese, Japanese, and Korean. See the official multilingual-E5 model card for the authoritative language list and model details.

Hinglish is code-switched Hindi and English rather than a separate model language. It is supported naturally because the same multilingual encoder sees both scripts and the same Unicode-aware lexical matcher sees words such as account, login, nahi, and रहा. Romanized Hindi spelling is variable, so include the spellings your users actually write in the examples.

Language support means the model can process text in those languages; it does not guarantee identical accuracy for every language. Performance may be lower for low-resource languages, short inputs, slang, transliteration, or domains that are absent from the examples. For production quality, provide several representative examples per class in every language and writing style you expect, then validate with a held-out test set.

Choosing a model and understanding latency

Semantra performs no language detection, translation, or second inference stage. Model selection happens when Classifier is created:

english = Classifier(model="english")       # default: smaller and faster
multilingual = Classifier(model="multilingual")  # mixed languages/Hinglish

Both modes use the same pipeline and expose the same Prediction object. The reported inference_time_ms includes query embedding, semantic similarity, BM25 scoring, score fusion, ranking, and threshold checks. It does not add a second model call or a separate timing pass; measuring the time uses only two monotonic clock reads.

The multilingual model has more model data than the English MiniLM model, so it normally requires more memory and can have higher CPU inference latency. There is no single fixed latency number: CPU model, thread settings, query length, number of examples, and warm-up all affect it. Benchmark on the deployment machine after one warm-up call:

from statistics import median
from semantra import Classifier

def benchmark(model_name):
    c = Classifier(model=model_name)
    c.add_class("support", ["I need help", "Mujhe madad chahiye"])
    c.add_class("billing", ["I have a payment question", "Payment ko lekar sawal hai"])
    c.predict("I need help")  # warm-up: excludes first-load cost
    times = [c.predict("Mujhe help chahiye").inference_time_ms for _ in range(30)]
    return median(times)

print("English median:", benchmark("english"), "ms")
print("Multilingual median:", benchmark("multilingual"), "ms")

The first prediction may be slower because ONNX Runtime initializes the local model. Call predict() once during application startup if predictable request latency matters. Class examples are embedded when added, so they should be loaded once and reused rather than rebuilding the classifier for every query.

Concurrency

After classes have been registered, a classifier can be shared by concurrent read-only prediction requests. Semantra protects one-time model initialization and ONNX Runtime handles concurrent inference calls. Do not call add_class() while another thread is predicting; finish configuration first, then share the classifier across worker threads or requests. If classes must change at runtime, build a new classifier and swap it into the application atomically.

Practical use cases

Semantra is suitable when a small set of labeled examples is available but training a dedicated model is unnecessary:

  • Customer-support intent routing across teams such as access, billing, delivery, returns, or technical support.
  • Helpdesk and IT ticket triage for password, device, network, and software issues.
  • Multilingual chatbot or AI-agent routing before a downstream workflow runs.
  • FAQ and knowledge-base category selection using examples written by domain experts.
  • Contact-center message tagging, escalation detection, and human handoff.
  • Form, email, or feedback classification where an Unknown result is safer than forcing an incorrect class.

Semantra is not a replacement for supervised training when you have a large, stable labeled dataset, strict regulatory calibration requirements, or highly specialized language. Use the confidence and margin thresholds, inspect top_k, and route uncertain predictions to clarification or human review.

Prediction results

Each prediction includes diagnostics for routing, auditing, or downstream business rules:

print(result.class_name)
print(result.confidence)
print(result.semantic_score)
print(result.lexical_score)
print(result.margin)
print(result.inference_time_ms)  # end-to-end prediction latency in milliseconds

for candidate in result.top_k:
    print(candidate.class_name, candidate.confidence)

confidence is a normalized ranking score, not a calibrated probability. semantic_score measures embedding similarity, lexical_score measures BM25 matching, and margin is the difference between the top two class scores. inference_time_ms measures end-to-end predict() latency, including query embedding and ranking, in milliseconds. It uses two monotonic clock reads and does not perform additional inference or searches, so its measurement overhead is negligible. For reliable benchmarks, measure multiple calls and report a median or percentile rather than relying on one prediction.

Unknown and ambiguity handling

By default, Semantra returns Unknown when the top score is below the minimum confidence or is too close to the runner-up:

from semantra import Classifier, ClassifierConfig

classifier = Classifier(config=ClassifierConfig(
    semantic_weight=0.75,
    lexical_weight=0.25,
    min_confidence=0.60,
    min_margin=0.08,
    top_k=3,
))

When a prediction is Unknown, result.top_k still contains the strongest candidates so an application can request clarification or route to a human.

Persistence

Build the classifier during deployment and persist it for fast process startup:

classifier.save("classifier.json")
restored = Classifier.load("classifier.json")
result = restored.predict("Please update the address for my order")

Persistence stores the examples, configuration, and precomputed embeddings. The model remains local and is used only to embed new queries.

The selected built-in model is saved with the classifier metadata, so loading a multilingual classifier automatically restores multilingual mode. You can override it with Classifier.load("classifier.json", model="english") when using a compatible persisted embedding matrix.

Custom components

The default embedding, lexical, vector, and fusion components are replaceable:

from semantra import Classifier, WeightedScoreFusion

classifier = Classifier(
    embedding_model=my_embedding_model,
    lexical_searcher=my_lexical_searcher,
    vector_searcher=my_vector_searcher,
    score_fusion=WeightedScoreFusion(semantic_weight=0.65, lexical_weight=0.35),
)

Custom embedding models must provide dimension and an embed(texts) method returning one NumPy vector per input text.

Google Colab test

Until a PyPI release is published, install directly from GitHub in a fresh Google Colab notebook:

!pip install -q "git+https://github.com/MANMEET75/semantra-classify.git"
from semantra import Classifier, ClassifierConfig

classifier = Classifier(config=ClassifierConfig(top_k=3))
classifier.add_class("account_access", [
    "I cannot sign in", "My account is locked", "The password reset is not working",
])
classifier.add_class("billing", [
    "I do not recognize this charge", "Please explain my invoice", "I need to change my payment method",
])
classifier.add_class("shipping", [
    "My package is late", "Where is my order?", "I need to update the delivery address",
])

for query in [
    "The password reset link does not work",
    "Why was I charged an extra fee?",
    "My parcel still has not arrived",
    "Tell me something unrelated to these topics",
]:
    result = classifier.predict(query)
    print(f"Query: {query}")
    print(f"Class: {result.class_name or 'Unknown'}")
    print(f"Confidence: {result.confidence:.3f}")
    print(f"Latency: {result.inference_time_ms:.2f} ms")
    print("Top candidates:", [(x.class_name, round(x.confidence, 3)) for x in result.top_k])
    print()
classifier.save("colab-classifier.json")
restored = Classifier.load("colab-classifier.json")
print(restored.predict("I am unable to access my profile").class_name)

For a multilingual Colab smoke test from GitHub, use the repository install shown above and Classifier(model="multilingual"). For PyPI, install both semantra-classify and semantra-classify-multilingual. The multilingual asset package is separate so each PyPI wheel remains within the file-size limit; both model packages execute fully offline after installation.

Design and performance

  • No training or fine-tuning is required.
  • Embeddings run locally with ONNX Runtime on CPU.
  • Example embeddings are precomputed when classes are added.
  • BM25 operates in memory over the supplied example corpus.
  • The default design targets small-to-medium few-shot corpora and low-latency application routing.
  • model="english" is the fastest, smallest built-in option; use model="multilingual" when language coverage is more important than model size.

Development and releases

pip install -e ".[dev]"
pytest
ruff check src tests
python -m build

Release versions are maintained in both pyproject.toml files and documented in CHANGELOG.md. Run python scripts/build_release.py to build the two release wheels. GitHub Actions tests Python 3.9–3.13 and publishes tagged releases through PyPI Trusted Publishing, without long-lived API tokens. The The two PyPI projects use separate GitHub workflows so each Trusted Publisher can be registered independently: publish.yml for the main package and publish-multilingual.yml for the companion package.

License

Semantra is released under Apache License 2.0. The bundled all-MiniLM-L6-v2 ONNX model is also distributed under Apache-2.0; see its NOTICE file.

Release files for semantra-classify 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for semantra-classify 0.3.0
File Interpreter ABI Platform
semantra_classify-0.3.0-py3-none-any.whl Python 3 none any Details

Release files / semantra_classify-0.3.0-py3-none-any.whl

Download URL semantra_classify-0.3.0-py3-none-any.whl
Size 83.1 MB
Tags Python 3
SHA-256 checksum
How to use checksums
7190399f6f550239700369ee3d6fa7722754182b3c33fef091c789d98ed5506d
BLAKE2b-256 checksum
How to use checksums
0e1177a527d1fa4e1f99921da40a2aacb14f46ee805bce18876c59958f31bad8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page