Skip to main content

Safety guardrails for LLM apps — PII redaction, prompt injection detection, ethical filtering and anti-hallucination

Project description

Ignis Guardrails: AI Safety & Compliance Framework

PyPI version Python License: MIT

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 async and 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:

  1. 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.

  2. 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:

  1. The main LLM generates a response to the user's query.
  2. 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.
  3. 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

  1. Confirm your .env file contains a valid OPENAI_API_KEY.
  2. Verify the key is active and not revoked on the OpenAI dashboard.
  3. 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:


Questions or bugs? Open an issue on the PyPI page.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ignis_guardrails-0.2.0.tar.gz (20.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

ignis_guardrails-0.2.0-py3-none-any.whl (16.3 kB view details)

Uploaded Python 3

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

Hashes for ignis_guardrails-0.2.0.tar.gz
Algorithm Hash digest
SHA256 cbd1b80e2b1177eac38e37bbfa4d1b6b5ad8e889de20467484479b1ecd90208c
MD5 b0dae99827fa69d2c96223408c0c6577
BLAKE2b-256 7bee86188553119c19b815c14734b8a32d05a41e22760be7fb001271b0fd79af

See more details on using hashes here.

File details

Details for the file ignis_guardrails-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for ignis_guardrails-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 f860caa22636a3f0d89792d0cf4d1aea06a158bf8bdf133e2f8fb4e0cda34298
MD5 ee6b337f07b35b866e33020613c87c0a
BLAKE2b-256 a100a6b9e4299e7832e38934a8c40b7359889ff4d804a4b80880a2ab363add6a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page