Content safety
Detect and mask PII and unsafe content.
Content safety scans the text going into and out of your agent, detects personally identifiable information (PII), secrets and prompt injection, and masks sensitive values before they are stored.
Report by default
By default Guard only reports findings to the dashboard. Text is hidden only if you ask for
redact, and a call is stopped only if you ask for block. You are never surprised by a stopped
agent.
What it finds
| Kind | Example | Default handling |
|---|---|---|
| Prompt injection | "Ignore your previous instructions", also in Hindi and Hinglish | Reported. Stops a call only in block mode. |
| Cross-prompt injection | A hidden instruction planted in a web page, document or tool result | Reported. Stops a call only in block mode. |
| Secrets | AWS keys, GitHub tokens, JWTs, private-key blocks, passwords next to a key name | Masked before storage |
| Personal data (PII) | Aadhaar, PAN, phone numbers, UPI IDs, card numbers, email | Masked before storage |
PII detection is built as country packs, and India is covered first: Aadhaar, PAN, GSTIN, IFSC, UPI ID, bank account, passport, voter ID, driving licence, vehicle registration, PIN code, UAN and ABHA. Two packs exist today, india and us. An organisation with no country chosen defaults to India.
To keep false alarms low, weak formats such as a bare 10-digit number are reported only next to a context word like "phone". Card numbers must pass the Luhn check and Aadhaar must pass the Verhoeff check. Digits written in Devanagari and other scripts are read as normal digits, and obvious placeholder values are ignored.
What is stored
- Raw personal data is never stored. Every finding is saved with the value already masked.
- Secrets in a trace are masked before the trace is written, so a raw key never reaches storage.
- The dashboard shows the masked text, the pattern, the severity, whether it was in the input or the output, and the trace it came from.
Check text yourself
Two methods on Guard check any text directly.
incidents = guard.check_content(user_message, direction="input")
if any(i["severity"] in ("high", "critical") for i in incidents):
return {"error": "This message could not be processed."}check_content reports findings and never changes the text. direction is "input" for text going into the model and "output" for text coming out.
result = guard.redact_content(user_message, direction="input")
reply = llm.generate(result.text) # never the original
if not result.complete:
... # masked locally, or too many values: take careredact_content returns text with every secret and PII value hidden.
| Field | Meaning |
|---|---|
text | The safe text |
complete | False means it was not fully checked. Do not treat it as certified clean. |
incidents | The findings |
source | "server", or "local" if Guard could not be reached |
If Guard is unreachable, the SDK masks with its own local rules. That hides secrets and PII but cannot see prompt injection. These methods never raise on a network problem and never return the original text as if it were redacted.
Tools: arguments and results
@guard.tool checks both what goes into a tool and what comes back.
from impraint_guard.core.tool import ImpraintGuardUnsafeResult
@guard.tool(on_sensitive="redact", on_result="block")
def fetch_page(url: str) -> str:
return requests.get(url).text
try:
page = fetch_page("https://example.com/offer")
except ImpraintGuardUnsafeResult as e:
print(e.categories) # for example ['cross_prompt_injection']| Mode | What happens |
|---|---|
off | Nothing is checked |
report (default) | Checked in the background, so the agent never waits. Findings go to the dashboard. |
redact | Checked first. The agent gets a copy with secrets and PII hidden. |
block | Checked first. The agent gets an exception instead of the unsafe value. |
on_sensitivecovers the arguments. A value under a key named like a secret (password,token,Authorization) is hidden by name.on_resultcovers what the tool returns. Inblockmode, a result containing customer data is stopped too, so useredactfor tools that return customer data on purpose.- Exceptions carry paths and categories, never the values.
Text going into the model
Prompt injection is most dangerous on the way in: a web page, document or tool result carries a hidden instruction into the next model call. Input screening is already on in report mode. Choose how it behaves with on_input:
from impraint_guard.integrations.gemini import patch_gemini
patch_gemini(guard, on_input="block") # whole process
# or: guard.wrap_gemini(client, on_input="block")
# or: ImpraintGuardCallbackHandler(guard, on_input="block")| Mode | What happens |
|---|---|
report (default) | Checked in the background. The call is never delayed. |
block | Checked first. High or critical prompt injection, or any hidden instruction planted in content, raises ImpraintGuardUnsafeInput. |
off | Nothing is checked |
- With
fail_open=False, a call is also stopped when Guard cannot be reached. - A secret or PII value in the user's own message does not stop the call in
blockmode. Useredact_contenton it first. - Not read: your system prompt, the model's earlier replies, and images, audio or files. Retrieved documents are checked where they enter your agent.
- The same text is not checked twice within a trace, so resent chat history adds no extra requests.
- At most 200 text values and 100,000 characters are checked per call.
Known limits in beta
- Detection is a pattern library. It catches known wordings well and unknown wordings poorly. Names and street addresses in free text are not detected yet.
- Only
indiaanduscountry packs exist. - Input screening and the options above are supported for Gemini, LangChain and LangGraph. Anthropic and OpenAI come after beta.
- Custom rules, the allowlist and the country choice are changed by admins through the API. The dashboard has no screens for them yet.
- Secrets and PII found in a tool result are hidden, blocked or logged, but not yet saved as dashboard incidents.