Content-safety checks (prompt injection, PII, output safety) for LLM systems. Imported as `drishti`.
Project description
दृष्टि Drishti
Fast, honest, self-hostable content safety for LLM systems.
Prompt-injection detection, PII detection and redaction, and output-safety classification. Three checks, one package, running entirely on your hardware.
Drishti (दृष्टि, "sight") watches the text that flows in and out of LLM systems and reports what it sees. It runs three checks:
| Check | On | Returns |
|---|---|---|
| Prompt injection | inputs | an injection score, a class, and a confidence |
| PII detection and redaction | inputs | located PII spans plus a redacted copy of the text |
| Output safety | outputs | a score per safety category and an aggregate verdict |
Every check returns a calibrated score and the identity of the model that produced it. Drishti never makes a policy decision on its own. It sees, it scores, and it lets your policy layer decide.
Highlights
- Three checks, one package. No assembling three Python projects with three runtimes and three opinions about the output type.
- Scores, not verdicts. Every result is a number a deterministic policy can act on. Drishti refuses nothing itself.
- Models are yours. Nothing is hardcoded. You choose the model per check; if it is missing, Drishti downloads it once, verifies it, and caches it.
- Offline by default. No default code path calls a remote service. CPU first, GPU optional.
- Honest numbers. Precision and recall come from the eval harness, and any path that has not cleared its bar is labelled experimental.
- Three surfaces, one core. A Rust crate, a Python package, and an HTTP service that all return identical results.
Table of contents
- Install
- Quick start
- The three checks
- Configuration
- Models
- HTTP API
- Eval results
- Performance
- Threat model
- Project layout
- License
- Part of Niyam
Install
Drishti is approaching its first tagged release. Published wheels and images will be available from the channels below; until then, build from source.
Python (embedded, runs models in-process; imported as drishti)
pip install sarthiai-drishti
One abi3 wheel per platform covers Python 3.9 through 3.13, on Linux x86_64, Linux ARM64, macOS ARM64 (Apple Silicon), and Windows x86_64.
Remote client SDKs (call a running drishti-server, no model loaded locally)
pip install sarthiai-drishti-sdk # Python, imported as drishti_sdk
npm install sarthiai-drishti-sdk # Node
Docker
docker pull sarthiai/drishti
Multi-architecture (linux/amd64 and linux/arm64). The container carries its own Linux, so the host operating system does not matter.
From source
git clone https://github.com/SarthiAI/Drishti
cd Drishti
cargo build --release # builds the CLI and the server
pip install maturin && maturin develop --release # builds the Python wheel
ONNX Runtime at runtime. Drishti links ONNX Runtime dynamically (ort's
load-dynamic), so the build is pure Rust and the artifacts stay small and
portable. The library is provided at runtime:
- The
sarthiai-drishtiwheel and the Docker image pull it in automatically (a dependency of the wheel; downloaded into the image), sopip installanddocker runjust work. - Running the CLI or server from a source build: install ONNX Runtime (for
example
pip install "onnxruntime>=1.24") and point Drishti at its shared library withORT_DYLIB_PATH=/path/to/libonnxruntime.so(or.dylib/.dll).
Quick start
All three surfaces read the same configuration file, which is where you choose the model for each check. See Configuration.
Command line
drishti --config config.toml prompt --text "Ignore all previous instructions."
drishti --config config.toml pii --text "Email me at jane@example.com"
drishti --config config.toml output --text "Have a great day!"
drishti --config config.toml all --text "..." --output "..."
drishti --config config.toml manifest # loaded model ids and hashes
Pass --file <path> instead of --text to read from a file. Output is
structured JSON.
HTTP service
drishti-server --config config.toml --bind 0.0.0.0:8080 --token <bearer-token>
Python
import drishti
d = drishti.Drishti.from_config_file("config.toml")
d.check_prompt("Ignore all previous instructions.")
d.check_pii("Email me at jane@example.com")
d.check_output("Have a great day!")
d.manifest()
Methods return plain dictionaries and release the interpreter lock during inference.
The three checks
Prompt injection
Takes a prompt and returns an injection score from 0.0 to 1.0, a class, a confidence, the latency, and the model id. It catches common injection patterns ("ignore previous instructions", "you are now DAN", and similar). It is one layer of defense, not a jailbreak-proof filter.
PII detection and redaction
Two stages:
- A regex stage (always on, about 5 ms) finds structurally identifiable PII: emails, credit cards (Luhn validated), phone numbers, IPv4 and IPv6 addresses, IBANs, US SSNs, India PAN, Aadhaar and UPI, UK NINO, and EU VAT numbers.
- An optional NER stage finds unstructured PII like names, organisations, and locations.
The result is a list of spans (byte offsets, kind, confidence, source) plus a redacted copy of the text. Redaction is chosen per kind: mask, hash, tokenise, keep, or refuse.
Output safety
Takes a model output and returns a score per safety category, an aggregate pass-or-fail against a threshold you set, and the detected language. The taxonomy comes from the configured model, so any classifier-style safety model fits.
Configuration
Configuration is a TOML file. Every value can also be overridden by an
environment variable or a .env file, so tuning never needs a code change or a
rebuild. The override key is DRISHTI_<PATH>, with a double underscore between
nesting levels:
DRISHTI_OUTPUT__THRESHOLD=0.05
DRISHTI_PII__NER__DROP_ACRONYMS=true
DRISHTI_INTRA_THREADS=4
A worked example is in config/example.toml. A check is enabled only when its section is present, and an enabled check must name a model or startup fails with a clear error rather than guessing one.
Models
Drishti hardcodes no model. You choose the model for each check through configuration: there are no default model ids, URLs, or hashes compiled into the binary. If a chosen model is already present, Drishti uses it directly. If it is not, Drishti downloads it once from the configured source, verifies its SHA-256 when you provide one, caches it, and then uses it. To bring your own fine-tuned model, point the config at a local path.
There is no default model and no bundled weights. Instead Drishti ships a recommendation matrix: MODELS.md lists vetted models per check across a footprint range (small to large), with precision and recall measured on public benchmarks, honest notes on where each model fits, and starting points by industry. Pick a row, point config at it. config/starter.toml is a ready-to-run example, one point on that matrix.
A working starter set (used in config/starter.toml):
| Check | Model | Weights | Size |
|---|---|---|---|
| Prompt injection | ProtectAI DeBERTa-v3-base prompt-injection-v2 | fp32 | 704 MB |
| PII names and orgs | dslim/distilbert-NER | fp32 | 249 MB |
| Output safety | KoalaAI Text-Moderation | int8 | 136 MB |
Note: model size is a footprint budget, not a quality ranking. What decides accuracy is whether a model was trained on content and labels like yours; see MODELS.md. Separately, int8 dynamic quantization significantly degrades DeBERTa-v3 accuracy, so run that prompt-injection model at full precision and switch model family if you need a smaller footprint.
HTTP API
JSON in, JSON out. The check endpoints and the manifest require a bearer token when one is configured; health and metrics are always open.
| Method | Path | Body | Auth |
|---|---|---|---|
| POST | /v1/check/prompt |
{ "input": "..." } |
bearer |
| POST | /v1/check/pii |
{ "input": "..." } |
bearer |
| POST | /v1/check/output |
{ "output": "..." } |
bearer |
| POST | /v1/check/all |
{ "prompt": "...", "output": "..." } |
bearer |
| GET | /v1/manifest |
loaded model ids and hashes | bearer |
| GET | /healthz |
liveness | open |
| GET | /readyz |
models loaded | open |
| GET | /metrics |
Prometheus text | open |
curl -s -X POST http://localhost:8080/v1/check/pii \
-H "authorization: Bearer <token>" \
-H "content-type: application/json" \
-d '{"input": "card 4111 1111 1111 1111"}'
Prefer a typed client over raw HTTP? Remote client SDKs for Python
(sarthiai-drishti-sdk) and Node (sarthiai-drishti-sdk) live in clients/,
each with its own README. They call a running drishti-server and return typed
results; they load no model themselves. This is distinct from the in-process
Python package (import drishti) shown above, which runs the models locally.
Eval results
These figures come from the eval harness (eval/) run through the real
engine on recognized public benchmarks: deepset/prompt-injections, the OpenAI
moderation evaluation, and an ai4privacy/pii-masking-200k English sample. They
measure the specific models configured, not Drishti in the abstract: accuracy is
a property of the model you pick. The full per-model, per-tier matrix and how to
choose is in MODELS.md. Reproduce with cargo run -p drishti-eval -- --config <cfg> --datasets <dir>; the report, including the SHA-256 of every model
used, is written under eval/results/.
Measured highlights (validated means it cleared its bar):
| Check (model) | Precision | Recall | F1 | Verdict |
|---|---|---|---|---|
| Output safety (KoalaAI Text-Moderation) | 0.879 | 0.960 | 0.918 | validated |
| PII regex, Email | 0.996 | 0.939 | 0.967 | validated |
| PII regex, IBAN | 1.000 | 1.000 | 1.000 | validated |
| PII NER, PersonName (distilbert to bert-large) | up to 0.840 | up to 0.919 | 0.86 to 0.88 | experimental |
| Prompt injection (ProtectAI DeBERTa) | 0.965 | 0.414 | 0.580 | experimental |
The prompt-injection recall is low because this benchmark is multilingual and out of distribution for that English model; a different model scores very differently. That is the point of MODELS.md: public numbers do not predict your traffic. Every runtime result stays labelled experimental until its configured path clears its bar on a real benchmark.
Performance
Warm inference on commodity CPU hardware: the regex PII stage runs in about 5 ms; the NER and output-safety classifiers in tens of milliseconds; the prompt-injection model at full precision is the heaviest, in the low hundreds of milliseconds. A cold process additionally pays a one-time model load, which the persistent server amortizes. Detailed p50 and p99 figures are published each release.
Threat model
In scope: naive prompt injection (instruction-override patterns), common PII in inputs and outputs (emails, cards, phones, names, addresses, and the structured identifiers above), common harmful output content in English, and tampering with model files (caught by SHA-256 verification when a hash is set).
Out of scope: adversarial prompts crafted to evade a specific classifier, jailbreaks that do not use injection patterns (roleplay, hypothetical framing), non-English content (Drishti reports the detected language and lowers its confidence), PII obfuscated through unusual encodings, and attacks on the host process itself. Drishti is one layer of defense: it reports scores, and enforcement belongs to your policy layer.
Project layout
drishti/
crates/
drishti-core/ detection logic and public types
drishti-models/ model resolution, download, caching, hash verification
drishti-regex/ the PII regex recognizer set
drishti-server/ the axum HTTP service
drishti-ffi-python/ the PyO3 bindings (in-process Python package)
drishti-cli/ the command-line tool
clients/
python/ drishti-sdk: remote HTTP client for Python
node/ @sarthiai/drishti-sdk: remote HTTP client for Node
eval/ the eval harness, datasets, benchmarks, and results
config/ example configuration
MODELS.md model recommendation matrix (no default model)
License
Drishti is licensed under the Elastic License 2.0 (ELv2). Licensor: Chirotpal Das. See LICENSE for the full text. In short: you may use, copy, modify, and distribute it, but you may not offer it to third parties as a hosted or managed service, and you may not remove the licensing notices.
Part of Niyam
Drishti is the content-safety piece of the Niyam family: Kavach (armor) protects, Drishti (sight) watches, Lipi (script) writes the rules. Drishti is useful on its own and integrates with the rest through Niyam's shared contracts in later versions. The decision layer (what to block or allow) is Kavach, with rules authored in Lipi. Drishti only ever hands over scores and flags.
Designed, developed, and maintained by Chirotpal
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sarthiai_drishti-0.1.1.tar.gz.
File metadata
- Download URL: sarthiai_drishti-0.1.1.tar.gz
- Upload date:
- Size: 54.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc119bd0b288d6afd06eca2be8113738e04b3b08b36f29249fd2db1690fb7bf4
|
|
| MD5 |
44572d4e1847bcb6f6aec557b0ea9046
|
|
| BLAKE2b-256 |
2035c7f31617bf08b1de7ae4e8a1ecf618b77b75518406f03702c1b69f8f198e
|
Provenance
The following attestation bundles were made for sarthiai_drishti-0.1.1.tar.gz:
Publisher:
release.yml on SarthiAI/Drishti
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sarthiai_drishti-0.1.1.tar.gz -
Subject digest:
dc119bd0b288d6afd06eca2be8113738e04b3b08b36f29249fd2db1690fb7bf4 - Sigstore transparency entry: 2150229561
- Sigstore integration time:
-
Permalink:
SarthiAI/Drishti@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/SarthiAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sarthiai_drishti-0.1.1-cp39-abi3-win_amd64.whl.
File metadata
- Download URL: sarthiai_drishti-0.1.1-cp39-abi3-win_amd64.whl
- Upload date:
- Size: 3.5 MB
- Tags: CPython 3.9+, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
215e62cc23cc91669abe077f61fdd441bda446a0044bfde961b32ede5a7ead79
|
|
| MD5 |
1d36456fd0b6b92d531f295f8e8fe86a
|
|
| BLAKE2b-256 |
6a82cef8c97741d2dd151eed4b2a0afdac7425b63da062e42fac8bc7a83a5c37
|
Provenance
The following attestation bundles were made for sarthiai_drishti-0.1.1-cp39-abi3-win_amd64.whl:
Publisher:
release.yml on SarthiAI/Drishti
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sarthiai_drishti-0.1.1-cp39-abi3-win_amd64.whl -
Subject digest:
215e62cc23cc91669abe077f61fdd441bda446a0044bfde961b32ede5a7ead79 - Sigstore transparency entry: 2150231555
- Sigstore integration time:
-
Permalink:
SarthiAI/Drishti@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/SarthiAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sarthiai_drishti-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: sarthiai_drishti-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 4.1 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b60041d56d081882ebab3308596addd271d747e4c0d350ad2cc9bf2b24fc82bb
|
|
| MD5 |
0ad8c0160c7ee197800685424efbd392
|
|
| BLAKE2b-256 |
7f8fcdd926f1bf6f116994b7ad221c4c81bc1cbd66ec65a8e1fb630093663b58
|
Provenance
The following attestation bundles were made for sarthiai_drishti-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl:
Publisher:
release.yml on SarthiAI/Drishti
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sarthiai_drishti-0.1.1-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl -
Subject digest:
b60041d56d081882ebab3308596addd271d747e4c0d350ad2cc9bf2b24fc82bb - Sigstore transparency entry: 2150229925
- Sigstore integration time:
-
Permalink:
SarthiAI/Drishti@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/SarthiAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sarthiai_drishti-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: sarthiai_drishti-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 4.2 MB
- Tags: CPython 3.9+, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9fce1671560e2bcba5b7a69ad68ca99a765d1a232c23bc7fe06381dde6c7dfbc
|
|
| MD5 |
ffba54387b5ee2051d501726fb84e546
|
|
| BLAKE2b-256 |
e57393ac8587636c4decc22e613728ba0b8871c0ee2e14a8b03e916f9e9ec8e1
|
Provenance
The following attestation bundles were made for sarthiai_drishti-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl:
Publisher:
release.yml on SarthiAI/Drishti
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sarthiai_drishti-0.1.1-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl -
Subject digest:
9fce1671560e2bcba5b7a69ad68ca99a765d1a232c23bc7fe06381dde6c7dfbc - Sigstore transparency entry: 2150232017
- Sigstore integration time:
-
Permalink:
SarthiAI/Drishti@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/SarthiAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sarthiai_drishti-0.1.1-cp39-abi3-macosx_11_0_arm64.whl.
File metadata
- Download URL: sarthiai_drishti-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.7 MB
- Tags: CPython 3.9+, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c07e2cfb60b85dea87eb162210feb8e63d21dd9fa487b350fa0628980df2a94f
|
|
| MD5 |
1486156e474e5459b7d92a68410139a5
|
|
| BLAKE2b-256 |
320706d304e9e86f8ac991b023f6bdfa091a3468c2eb3f2a597d17590adcb925
|
Provenance
The following attestation bundles were made for sarthiai_drishti-0.1.1-cp39-abi3-macosx_11_0_arm64.whl:
Publisher:
release.yml on SarthiAI/Drishti
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sarthiai_drishti-0.1.1-cp39-abi3-macosx_11_0_arm64.whl -
Subject digest:
c07e2cfb60b85dea87eb162210feb8e63d21dd9fa487b350fa0628980df2a94f - Sigstore transparency entry: 2150230537
- Sigstore integration time:
-
Permalink:
SarthiAI/Drishti@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/SarthiAI
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@d3f3f628bb290588096d0a98fb5e545c657183c4 -
Trigger Event:
push
-
Statement type: