Metadata-Version: 2.4
Name: ignis_guardrails
Version: 0.2.0
Summary: Safety guardrails for LLM apps — PII redaction, prompt injection detection, ethical filtering and anti-hallucination
Author: Infogain-GenAI
License-Expression: MIT
Project-URL: Homepage, https://github.com/Infogain-GenAI/ignis_guardrails
Project-URL: Repository, https://github.com/Infogain-GenAI/ignis_guardrails.git
Project-URL: Documentation, https://github.com/Infogain-GenAI/ignis_guardrails#readme
Project-URL: Bug Tracker, https://github.com/Infogain-GenAI/ignis_guardrails/issues
Keywords: guardrails,llm,openai,anthropic,gemini,async
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Security
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: spacy<3.8.0,>=3.7.2
Requires-Dist: presidio-anonymizer==2.2.359
Requires-Dist: presidio-analyzer==2.2.359
Requires-Dist: torch==2.7.1
Requires-Dist: yara-python==4.5.4
Requires-Dist: nemoguardrails==0.14.1
Requires-Dist: langchain-openai<1.0.0,>=0.2.12
Requires-Dist: huggingface-hub[inference]<1.0.0,>=0.23.2
Requires-Dist: huggingface_hub[hf_xet]
Requires-Dist: python-dotenv>=1.0.0
Requires-Dist: pydantic-settings>=2.0.0
Requires-Dist: numpy<2,>=1.26
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Dynamic: license-file

# Ignis Guardrails: AI Safety & Compliance Framework

[![PyPI version](https://img.shields.io/pypi/v/ignis_guardrails.svg)](https://pypi.org/project/ignis_guardrails/)
[![Python](https://img.shields.io/pypi/pyversions/ignis_guardrails.svg)](https://pypi.org/project/ignis_guardrails/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/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](#quick-start)
- [Configuration](#configuration)
- [Guardrails Reference](#guardrails-reference)
  - [1. PII Redaction](#1-pii-redaction)
  - [2. Security Guardrails](#2-security-guardrails)
  - [3. Ethical Safeguard](#3-ethical-safeguard)
  - [4. Anti-Hallucination](#4-anti-hallucination)
- [Response Schema](#response-schema)
- [Running Tests](#running-tests)
- [Troubleshooting](#troubleshooting)

---

## Quick Start

### 1. Installation

```bash
pip install ignis_guardrails
```

After installation, download the required spaCy language model:

```bash
python -m spacy download en_core_web_lg
```

For development:

```bash
pip install "ignis_guardrails[dev]"
```

### 2. Configure Environment

Create a `.env` file in your working directory:

```ini
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

```python
{
    "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:**

```python
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:**

```python
user_input = "What is the weather like today?"
result = await pii_redaction(user_input)
# result["metadata"]["pii_redacted"] == False
```

**With retrieval context:**

```python
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

```python
{
    "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:**

```python
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:
```python
{
    "provider": "nemo_guardrails",
    "type": "Injection Detection",
    "subtype": "code_execution",
    "subtype_category": "Code",
    "action": "reject"
}
```

**SQL injection attempt — blocked:**

```python
user_input = "drop database users;"
result = await detect_prompt_injection(user_input)
# metadata["subtype_category"] == "SQLI"
# metadata["action"] == "reject"
```

**Legitimate networking code — allowed:**

```python
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

```python
{
    "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:**

```python
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:
```python
{
    "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:**

```python
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:**

```python
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

```python
{
    "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:**

```python
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:
```python
{
    "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:**

```python
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:

```bash
pip install "ignis_guardrails[dev]"
pytest
```

---

## Troubleshooting

### `ModuleNotFoundError: No module named 'ignis_guardrails'`

Reinstall the package:
```bash
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](https://platform.openai.com/api-keys).
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:
```python
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:
```bash
python -m spacy download en_core_web_lg
```

---

## License

This project is licensed under the [MIT License](https://opensource.org/licenses/MIT).

## References

Built with:
- [NVIDIA NeMo Guardrails](https://developer.nvidia.com/nemo-guardrails) — Colang-based rail framework
- [Microsoft Presidio](https://microsoft.github.io/presidio/) — PII detection and anonymisation engine
- [OpenAI](https://openai.com) — GPT-4o for content filtering and LLM inference
- [LangChain OpenAI](https://python.langchain.com/docs/integrations/llms/openai) — LLM abstraction layer
- [spaCy](https://spacy.io) — NLP pipeline for named entity recognition

---

**Questions or bugs?** Open an issue on the [PyPI page](https://pypi.org/project/ignis_guardrails/).
