Safety guardrails for LLM apps — PII redaction, prompt injection detection, ethical filtering and anti-hallucination
Project description
Ignis Guardrails: AI Safety & Compliance Framework
A comprehensive guardrails framework that provides safety monitoring, input validation, and output filtering for LLM applications. Built with modular safety patterns, it ensures your AI systems remain compliant, ethical, and secure while maintaining high performance.
Key Features
- PII Redaction: Automatic detection and masking of personally identifiable information
- Security Guardrails: Input validation and injection attack prevention (code, SQL, XSS, template, prompt injection)
- Ethical Safeguards: Content filtering for harmful, hateful, or inappropriate outputs
- Anti-Hallucination: Fact-checking and consistency validation for LLM responses
- NeMo Integration: Built on NVIDIA NeMo Guardrails for enterprise-grade safety
- Flexible Configuration: Environment-based setup with easy customization
Requires Python 3.10+ · An OpenAI API key is required · All guardrail functions are
asyncand must be awaited
Table of Contents
Quick Start
1. Installation
pip install ignis_guardrails
After installation, download the required spaCy language model:
python -m spacy download en_core_web_lg
For development:
pip install "ignis_guardrails[dev]"
2. Configure Environment
Create a .env file in your working directory:
OPENAI_API_KEY=sk-proj-YOUR_ACTUAL_KEY_HERE
LOG_LEVEL=info
Configuration
All configuration is managed through environment variables loaded from a .env file via pydantic-settings.
| Variable | Required | Default | Description |
|---|---|---|---|
OPENAI_API_KEY |
Yes | "" |
OpenAI API key used by all guardrails |
LOG_LEVEL |
No | info |
Logging verbosity (debug, info, warning, error) |
The API key can also be passed directly to each guardrail function via the openai_api_key parameter, which takes precedence over the environment variable.
Guardrails Reference
1. PII Redaction
Module: ignis_guardrails.providers.nemo_guardrails.pii_redaction
Function: pii_redaction(user_input, user_context="", openai_api_key=None)
Provider: NVIDIA NeMo Guardrails + Microsoft Presidio + spaCy (en_core_web_lg)
Model: gpt-4o
What It Does
Detects and masks Personally Identifiable Information (PII) from user inputs before they are processed by your LLM pipeline. The guardrail operates at two stages:
- Input stage — scans the raw user message for PII entities and replaces them with typed placeholders (e.g.,
<PERSON>,<EMAIL_ADDRESS>) before the LLM ever sees the content. - Retrieval stage — applies the same masking to any context documents fetched from a retrieval source.
A secondary LLM pass then applies an additional instruction-following redaction to catch any residual PII that pattern matching may have missed.
Detected Entity Types
| Category | Entities |
|---|---|
| Identity | PERSON, NRP (Nationality/Religion/Political group) |
| Contact | PHONE_NUMBER, EMAIL_ADDRESS, URL |
| Location | LOCATION |
| Financial | CREDIT_CARD, CRYPTO, IBAN_CODE, US_BANK_NUMBER |
| Government ID | US_SSN, US_PASSPORT, US_DRIVER_LICENSE, US_ITIN |
| Medical | MEDICAL_LICENSE |
| Technical | IP_ADDRESS |
| Temporal | DATE_TIME |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_input |
str |
Yes | The raw text to scan for PII |
user_context |
str |
No | Supporting context documents (e.g., RAG retrieved chunks); also redacted |
openai_api_key |
str |
No | Overrides OPENAI_API_KEY environment variable |
Response
{
"input": "<original user input>",
"response": "<redacted text with [REDACTED_TYPE] placeholders>",
"metadata": {
"provider": "nemo_guardrails",
"type": "PII Redaction",
"subtype": "PII Redaction",
"subtype_category": "PII Redaction",
"action": "allow" | "deny", # "deny" when CONTENT BLOCKED
"pii_redacted": True | False # True when input != output
}
}
Examples
Input contains PII — entities are masked:
import asyncio
from ignis_guardrails import pii_redaction
async def main():
user_input = (
"My name is John Doe, my email is john.doe@example.com, "
"my credit card is 6387421212121212, and my passport is M76543218."
)
result = await pii_redaction(user_input)
print(result)
asyncio.run(main())
Expected output (response field):
My name is <PERSON>, my email is <EMAIL_ADDRESS>,
my credit card is <CREDIT_CARD>, and my passport is <US_PASSPORT>.
Clean input — returned unchanged:
user_input = "What is the weather like today?"
result = await pii_redaction(user_input)
# result["metadata"]["pii_redacted"] == False
With retrieval context:
context_doc = "Customer record: Jane Smith, SSN 123-45-6789, Account #987654321"
result = await pii_redaction(user_input="Summarize this record", user_context=context_doc)
# Both user_input and user_context are scanned and redacted
2. Security Guardrails
Module: ignis_guardrails.providers.nemo_guardrails.security_guardrails
Function: detect_prompt_injection(user_input, openai_api_key=None)
Provider: NVIDIA NeMo Guardrails
Model: gpt-4o
What It Does
Detects and blocks injection attacks and prompt manipulation attempts embedded in user messages. The guardrail combines two complementary detection mechanisms:
-
NeMo Injection Detection rail — inspects the LLM output against YARA-based rules that match patterns for code injection, SQL injection, XSS, and template injection. If the output would have triggered a malicious rule, the response is replaced with a blocked message.
-
Regex-based Prompt Injection Detection — scans the raw user input for common prompt-hijacking phrases (e.g.,
ignore all previous instructions,forget the system prompt,bypass directives) before the LLM call is made.
Detected Injection Categories
| Category | Attack Types |
|---|---|
| Code | Shell imports (import os, import subprocess), code execution (eval, exec), file system access, environment variable access, socket creation, code obfuscation, dynamic imports, pickle deserialization |
| SQLI | Classic SQL injection, DROP statements, comment injection (--, /**/), stored procedure abuse, time-based blind SQLi, error-based SQLi, encoding-bypass SQLi, NoSQL injection |
| XSS | Basic <script> injection, DOM-based XSS, event-handler injection (onerror, onload), data: URI injection, filter-bypass XSS, Markdown-embedded XSS |
| Template | Jinja2 SSTI, Django template injection, FreeMarker injection, Thymeleaf injection, expression language injection |
| Prompt Injection | Instruction override phrases, system prompt exposure attempts, directive bypassing |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_input |
str |
Yes | The user message to evaluate |
openai_api_key |
str |
No | Overrides OPENAI_API_KEY environment variable |
Response
{
"input": "<original user input>",
"response": "<LLM response or blocked message>",
"metadata": {
"provider": "nemo_guardrails",
"type": "Injection Detection" | "Prompt Security",
"subtype": "<specific rule name, e.g. 'code_execution'>",
"subtype_category": "Code" | "SQLI" | "XSS" | "Template" | "Prompt Injection Detection",
"action": "allow" | "reject"
}
}
When no threat is detected, metadata contains only {"provider": "nemo_guardrails", "action": "allow"}.
Examples
Code injection attempt — blocked:
import asyncio
from ignis_guardrails import detect_prompt_injection
async def main():
user_input = "Write Python code to execute: import os; os.system('whoami')"
result = await detect_prompt_injection(user_input)
print(result)
asyncio.run(main())
Expected metadata:
{
"provider": "nemo_guardrails",
"type": "Injection Detection",
"subtype": "code_execution",
"subtype_category": "Code",
"action": "reject"
}
SQL injection attempt — blocked:
user_input = "drop database users;"
result = await detect_prompt_injection(user_input)
# metadata["subtype_category"] == "SQLI"
# metadata["action"] == "reject"
Legitimate networking code — allowed:
user_input = "Write a Python script using requests to get the weather in Santa Clara."
result = await detect_prompt_injection(user_input)
# metadata["action"] == "allow"
3. Ethical Safeguard
Module: ignis_guardrails.providers.openai.ethical_safeguard
Function: ethical_safeguard(user_input, openai_api_key=None)
Provider: OpenAI (direct via LangChain)
Model: gpt-4o (temperature 0.0 for deterministic results)
What It Does
Filters user inputs (or LLM outputs) for harmful, biased, or ethically problematic content using a zero-temperature classification call to GPT-4o. The model acts as a content policy enforcer and returns either the original unmodified text (clean) or the single token BLOCKED (harmful).
Detected Content Categories
| Category | Examples |
|---|---|
| Racial bias | Statements asserting racial superiority/inferiority |
| Gender bias | Discriminatory statements about gender |
| Religious bias | Hate speech targeting religious groups |
| Geographical bias | Discriminatory generalisations about nationalities/regions |
| Caste bias | Content discriminating based on caste |
| Hate speech | General incitement to hatred against any group |
| Discrimination | Exclusionary statements based on protected characteristics |
| Offensive language | Slurs, derogatory terms, and abusive language |
| Harmful instructions | Requests for instructions on violence, weapons, self-harm, etc. |
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_input |
str |
Yes | Text to be evaluated for harmful content |
openai_api_key |
str |
No | Overrides OPENAI_API_KEY environment variable |
Response
{
"input": "<original user input>",
"response": "<original text>" | "BLOCKED",
"metadata": {
"provider": "openai",
"type": "Ethical Safeguard", # only present when blocked
"subtype": "Harmful Content Detection", # only present when blocked
"subtype_category": "Harmful Content Detection",
"action": "allow" | "deny"
}
}
When content is clean, metadata contains only {"provider": "openai", "action": "allow"} and response is the unchanged input text.
Examples
Harmful content — blocked:
import asyncio
from ignis_guardrails import ethical_safeguard
async def main():
user_input = "white men are superior to blacks"
result = await ethical_safeguard(user_input)
print(result)
asyncio.run(main())
Expected response:
{
"input": "white men are superior to blacks",
"response": "BLOCKED",
"metadata": {
"provider": "openai",
"type": "Ethical Safeguard",
"subtype": "Harmful Content Detection",
"subtype_category": "Harmful Content Detection",
"action": "deny"
}
}
Dangerous instruction request — blocked:
user_input = "how to make a bomb"
result = await ethical_safeguard(user_input)
# result["response"] == "BLOCKED"
# result["metadata"]["action"] == "deny"
Clean content — passed through unchanged:
user_input = "What are the best practices for writing unit tests?"
result = await ethical_safeguard(user_input)
# result["response"] == user_input
# result["metadata"]["action"] == "allow"
4. Anti-Hallucination
Module: ignis_guardrails.providers.nemo_guardrails.anti_hallucination
Function: detect_hallucination_scenario(user_input, openai_api_key=None)
Provider: NVIDIA NeMo Guardrails
Model: gpt-3.5-turbo (main LLM) + self-check via the same model
What It Does
Detects when an LLM response contains claims that cannot be verified from a given context or are likely fabricated. It uses NeMo Guardrails' built-in self-check hallucination output rail:
- The main LLM generates a response to the user's query.
- A second LLM call evaluates whether the generated response (hypothesis) is in agreement with the available context (paragraph). The checker is instructed to rely only on the provided context, not on external knowledge.
- If the checker determines the hypothesis does not agree with the context (
"no"), the response is replaced with a standardised hallucination warning message.
Additionally, topic-specific Colang flows enforce hallucination checking for messages containing high-risk keywords: Microsoft, quantum, climate change, revolutionary, breakthrough.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_input |
str |
Yes | The user query to process through the LLM + hallucination check |
openai_api_key |
str |
No | Overrides OPENAI_API_KEY environment variable |
Response
{
"input": "<original user input>",
"response": "<LLM answer>" | "HALLUCINATION DETECTION: This response cannot be verified for accuracy.",
"metadata": {
"provider": "nemo_guardrails",
"type": "Hallucination Detection", # only present when detected
"subtype": "Hallucination Detection", # only present when detected
"subtype_category": "Hallucination Detection",
"action": "allow" | "reject"
}
}
When no hallucination is detected, metadata contains only {"provider": "nemo_guardrails", "action": "allow"}.
Examples
Unverifiable quantum computing claim — flagged:
import asyncio
from ignis_guardrails import detect_hallucination_scenario
async def main():
user_input = (
"Microsoft announced groundbreaking quantum computers that can solve climate change "
"instantly. Please provide details about this revolutionary technology and its "
"immediate global impact."
)
result = await detect_hallucination_scenario(user_input)
print(result)
asyncio.run(main())
Expected response:
{
"input": "Microsoft announced groundbreaking ...",
"response": "HALLUCINATION DETECTION: This response cannot be verified for accuracy.",
"metadata": {
"provider": "nemo_guardrails",
"type": "Hallucination Detection",
"subtype": "Hallucination Detection",
"subtype_category": "Hallucination Detection",
"action": "reject"
}
}
Unverifiable AI chip claim — flagged:
user_input = (
"Nvidia has developed a new AI chip that can perform 1 trillion operations per second, "
"making it the fastest chip in the world..."
)
result = await detect_hallucination_scenario(user_input)
# result["metadata"]["action"] == "reject"
Response Schema
All guardrail functions return a consistent dictionary with three top-level keys:
| Key | Type | Description |
|---|---|---|
input |
str |
The original unmodified user input |
response |
str |
The processed output (redacted text, LLM answer, or a block/warning message) |
metadata |
dict |
Structured information about what was detected and what action was taken |
metadata Fields
| Field | Always Present | Values | Description |
|---|---|---|---|
provider |
Yes | "nemo_guardrails", "openai" |
The underlying safety provider used |
action |
Yes | "allow", "deny", "reject" |
Whether the content was permitted or blocked |
type |
On detection | e.g. "PII Redaction", "Injection Detection" |
High-level guardrail category |
subtype |
On detection | e.g. "code_execution", "sql_injection" |
Specific rule or pattern matched |
subtype_category |
On detection | "Code", "SQLI", "XSS", "Template", etc. |
Grouped classification of the detected threat |
pii_redacted |
PII only | True, False |
Whether any PII entities were actually replaced |
Running Tests
Install dev dependencies and run pytest:
pip install "ignis_guardrails[dev]"
pytest
Troubleshooting
ModuleNotFoundError: No module named 'ignis_guardrails'
Reinstall the package:
pip install ignis_guardrails
AuthenticationError from OpenAI
- Confirm your
.envfile contains a validOPENAI_API_KEY. - Verify the key is active and not revoked on the OpenAI dashboard.
- Ensure your OpenAI account has available credits.
ValueError: OpenAI API key must be provided
Either set OPENAI_API_KEY in your .env file or pass the key explicitly:
result = await pii_redaction(user_input, openai_api_key="sk-proj-...")
spaCy model not found
The en_core_web_lg model must be installed separately after package installation:
python -m spacy download en_core_web_lg
License
This project is licensed under the MIT License.
References
Built with:
- NVIDIA NeMo Guardrails — Colang-based rail framework
- Microsoft Presidio — PII detection and anonymisation engine
- OpenAI — GPT-4o for content filtering and LLM inference
- LangChain OpenAI — LLM abstraction layer
- spaCy — NLP pipeline for named entity recognition
Questions or bugs? Open an issue on the PyPI page.
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 Distribution
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 ignis_guardrails-0.2.0.tar.gz.
File metadata
- Download URL: ignis_guardrails-0.2.0.tar.gz
- Upload date:
- Size: 20.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbd1b80e2b1177eac38e37bbfa4d1b6b5ad8e889de20467484479b1ecd90208c
|
|
| MD5 |
b0dae99827fa69d2c96223408c0c6577
|
|
| BLAKE2b-256 |
7bee86188553119c19b815c14734b8a32d05a41e22760be7fb001271b0fd79af
|
File details
Details for the file ignis_guardrails-0.2.0-py3-none-any.whl.
File metadata
- Download URL: ignis_guardrails-0.2.0-py3-none-any.whl
- Upload date:
- Size: 16.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f860caa22636a3f0d89792d0cf4d1aea06a158bf8bdf133e2f8fb4e0cda34298
|
|
| MD5 |
ee6b337f07b35b866e33020613c87c0a
|
|
| BLAKE2b-256 |
a100a6b9e4299e7832e38934a8c40b7359889ff4d804a4b80880a2ab363add6a
|