Processors
Add redaction, output correction, policy checks, and routing controls to a chat completion.
How processors work
Processors run before or after the upstream model call. Enable them for an individual request with inferada.processors, or configure defaults on the model.
Six processors are currently available:
anonymisereplaces detected personal data before the model call, then restores matching placeholders in the response.haystackadds structured request context, with optional schema-guided PII redaction. See Haystack page.spellcheckcorrects spelling and grammar in the model response.pii_guardrejects a request when it detects PII. It does not redact the request.require_localrejects a request unless the selected upstream is classified as local.prompt_shieldchecks selected message roles for suspected prompt-injection attempts before the upstream model call.
Enable processors
Add the processor names to inferada.processors. If a processor is language-dependent, also set inferada.language:
{
"model": "qwen3.5-35b",
"messages": [
{ "role": "user", "content": "Jan Jansen woont in Amsterdam. Vat zijn adres samen." }
],
"inferada": {
"processors": ["anonymise", "spellcheck"],
"language": "nl"
}
}Scope requirements:
anonymiseprocessor- Requires the pii scope. Requires inferada.language.
haystackprocessor- Requires the pii scope (when redact:true, default). Requires inferada.language. Caller config under inferada.haystack.
spellcheckprocessor- Requires the language scope. Requires inferada.language.
pii_guardprocessor- Requires the pii scope. No language needed: it scans across every language the PII upstream supports.
require_localprocessor- Requires the completions scope (everyone has it). No language needed.
prompt_shieldprocessor- Requires the completions scope (everyone has it). No language needed: heuristics are pattern-based and the optional classifier inspects flattened text.
A missing scope returns 403 and identifies the processor. A missing or unsupported language returns 422 with the processor name and the upstream's supported languages.
Message roles and scanning
anonymise and pii_guard scan user and assistant messages. They skip system messages, which commonly contain static rules, templates, and synthetic examples. Keep runtime personal data in a scanned role.
- System messages should contain rules, behaviour, static definitions, and synthetic examples.
- User and assistant messages should carry runtime data that needs PII scanning.
Personal data in a system message is not detected or redacted by these processors.
prompt_shield inspects user and tool roles by default and skips the application's system prompt. If you template system content, see Prompt templating for the rendering rules.
anonymise: redact and restore
Use anonymise to replace detected personal data before the upstream model call. If the response contains those placeholders, the gateway restores the corresponding values before returning it to your application.
Placeholders use the format <INF_TYPE_N> (e.g. <INF_PERSON_1>, <INF_EMAIL_ADDRESS_2>). The INF_ namespace separates these placeholders from application tags such as <CUSTOMER>. Numbering is consistent across every message in the conversation: the same original value always resolves to the same tag, so <INF_PERSON_1> means the same person in message 0 and message 5.
Statistical detection can miss personal data. For sensitive workflows, consider a separate detection step with the PII endpoints, then choose the route based on the result. To reject a request when PII is detected, place pii_guard before anonymise.
spellcheck: polish the output
Use spellcheck to correct spelling and grammar in the model response before displaying it.
Because spellcheck needs the complete response text to work, the gateway buffers the response rather than streaming it. The response body sets inferada.stream_forced_buffered: true so your client can detect the change.
pii_guard: reject if PII is detected
Unlike anonymise, pii_guard does not modify the prompt. It rejects the request when detection finds PII, before the upstream model is called. Use it as a defense-in-depth policy check, optionally with require_local.
The guard scans each language listed in the PII upstream's supported_languages, so the request does not need to set inferada.language.
On block (HTTP 422):
{
"error": "request_blocked",
"processor": "pii_guard",
"reason": "pii_detected",
"detected_types": ["PERSON", "EMAIL_ADDRESS"],
"message": "Request blocked by pii_guard",
"request_id": "…"
}The response includes detected entity types, but not the detected values.
On pass-through:
{
"inferada": {
"processors": {
"pii_guard": {
"applied": true,
"pii_detected": false,
"languages_checked": ["en", "nl"],
"timings": { "pre_ms": 42, "total_ms": 42 }
}
}
}
}Like anonymise, pii_guard uses statistical detection. Some PII will slip through. Treat it as defence-in-depth, not as your sole safeguard on regulated data.
require_local: require a local route
require_local returns 422 unless an administrator has classified the selected completion upstream as processing = "local". The check depends on routing configuration, not prompt content.
This check depends on every upstream being explicitly classified. The gateway rejects unclassified upstreams (processing = null) instead of assuming they are local.
On block (HTTP 422):
{
"error": "request_blocked",
"processor": "require_local",
"reason": "upstream_not_local",
"detected_types": [],
"message": "Request blocked by require_local",
"request_id": "…"
}prompt_shield: inspect prompt-injection attempts
prompt_shield is a pre-completion content guard. It inspects the prompt for instruction-override, role-hijack, persona-attack, delimiter-injection, and encoded-payload patterns before the upstream model is called. Two layers run in order, and processing stops on the first detection:
- Heuristics: an in-process rule engine with no network call. Each rule is tied to a sensitivity level (
low/medium/max). - Classifier service: an opt-in deep-learning model (Meta's Llama Prompt Guard 2 prompt-injection classifier) running behind Inferada. The gateway only calls it when a
prompt_shieldservice has been provisioned for your organisation.
Configure the processor under inferada.prompt_shield. Request values override model defaults:
level"off" | "low" | "medium" | "max"- "medium" is recommended for chat. "max" raises sensitivity at the cost of more false positives. "off" disables the processor.
action"reject" | "respond"- "reject" returns HTTP 422 with the structured request_blocked body (default). "respond" returns HTTP 200 with a synthesized chat-shaped refusal; prefer it for end-user chat surfaces.
fail_mode"auto" | "open" | "closed"- "auto" (default) is level-aware: low/medium fail open, max fails closed. "open" always lets the request through if the classifier is unavailable; "closed" always blocks. Heuristics still run regardless.
classifier_timeout_msnumber- Hard timeout for the classifier call. Defaults to 3000, capped at 10000.
refusal_messagestring- Per-request refusal text for "respond" mode. When omitted, the gateway uses the model default or its built-in fallback.
Inspect roles default to ["user", "tool"]; the calling application's system prompt is treated as trusted.
On block with action: "reject" (HTTP 422):
{
"error": "request_blocked",
"processor": "prompt_shield",
"reason": "prompt_injection_suspected",
"detected_types": ["instruction_override", "role_hijack"],
"message": "Request blocked by prompt_shield",
"request_id": "…"
}On block with action: "respond" (HTTP 200), the gateway returns a standard OpenAI-shaped chat completion with finish_reason: "content_filter" and a localised refusal in the assistant content. Use this mode when a chat interface should handle the refusal as an assistant response instead of a transport error.
Matched detection data is retained server-side for audit and is not included in the client response.
Composing the guards
pii_guard evaluates content; require_local evaluates the selected route. Choose the combination that matches the policy:
["pii_guard"]content- Reject when PII is detected, regardless of whether the route is local or external.
["require_local"]routing- Require a local route without inspecting the prompt for PII.
["require_local", "pii_guard"]defence-in-depth- Require a local route and reject when PII is detected.
["pii_guard", "anonymise"]: the guard fires first and blocks if any PII is detected. anonymise never runs. ["anonymise", "pii_guard"]: anonymise redacts real values to <INF_…> tags first, so the guard sees placeholder tags and passes through. That ordering defeats the guard; avoid it.
Language support
Language-dependent processors (anonymise, spellcheck) require inferada.language on the request and verify it against the backing upstream's declared supported_languages. There are no hardcoded defaults: if the language isn't declared, the gateway rejects the request.
Admins declare the language set per upstream in the portal. New upstreams inherit their template's default set on creation and can be trimmed or extended from there. We ship defaults for the Presidio and LanguageTool templates.
pii_guard does not use the request language; it scans across the full supported_languages list regardless of what the caller sends. prompt_shield is also language-agnostic: heuristic patterns are language-independent and the classifier flattens text before scoring.
Reading processor metadata
When processors run, the response includes an inferada.processors object keyed by processor name:
{
"inferada": {
"processors": {
"anonymise": {
"applied": true,
"entities_redacted": 2,
"replacements_restored": 2,
"timings": { "pre_ms": 12, "post_ms": 8, "total_ms": 20 }
},
"spellcheck": {
"applied": true,
"corrections": 1,
"timings": { "post_ms": 25, "total_ms": 25 }
}
},
"stream_forced_buffered": true
}
}applied: whether the processor actually did something.entities_redacted/replacements_restored: counts from anonymise.corrections: count from spellcheck.pii_detected/languages_checked: from pii_guard on the pass-through path (blocks return a 422 instead of a response body).timings: latency added by the processor, in milliseconds.stream_forced_buffered: set to true when streaming was turned off because a processor needed the full response.
Usage & costs
Each processor counts toward its own scope's usage:
anonymiseandpii_guardadd to yourpiiusage.spellcheckadds to yourlanguageusage.require_localperforms a configuration check and does not add service usage.prompt_shieldheuristics run in process and do not add service usage. When configured, the classifier makes one service call per request and counts toward theprompt_shieldservice.
pii_guard runs one PII-analysis call per message per configured language. On an upstream with many languages, this adds up, so budget accordingly.
The completion request's history entry also records per-processor metrics inside its usage object. See Usage accounting for the exact shape.