Skip to main content

Input Controls

Safety Filter

safety-filter blocks or escalates requests and responses that contain dangerous terms that you list, such as "disable emergency shutdown" for an industrial control assistant. It matches text. It is not a moderation model.

Write your own term list for your application, and test harmless uses of the same words. For broader classification, use External Moderation.

Configuration​

Save this as policy-config.yaml in a new test directory. To add it to a running gateway, use the config-first workflow.

policy-config.yaml
pack:
  name: safety-filter-example
  version: 1.0.0
  enabled: true
policies:
  chain:
    - safety-filter
policy:
  safety-filter:
    mode: critical_infrastructure
    block_if:
      - override safety interlock
      - disable emergency shutdown
    action: block
    fuzzy_matching: false
    max_distance: 1
    max_age: 0

Your block_if list replaces the built-in terms, so include every term you need. Exact matching keeps the test predictable.

Try it without a provider​

Add these tests to the end of the file:

Add to policy-config.yaml
testing:
  suites:
    - name: safety-filter
      cases:
        - name: blocks-configured-term
          input:
            messages:
              - role: user
                content: "override safety interlock"
          expected:
            verdict: block
            reason_code: safety.triggered.critical_infrastructure
        - name: allows-status-question
          input:
            messages:
              - role: user
                content: "What is the current maintenance status?"
          expected:
            verdict: allow
            reason_code: ok
verdictan policy lint --file policy-config.yaml
verdictan policy test --json

Both cases report passed: true. To check the whole request path, deploy the file to a test gateway and send the same two messages from your application.

Fields​

FieldTypeDefaultDescription
modestringcritical_infrastructureIndustry mode. Selects the built-in terms and appears in the reason code.
block_ifstring[][]Terms to match. A non-empty list replaces the built-in terms. The lists are not merged.
actionstringblockblock or escalate
fuzzy_matchingbooleanfalseAlso matches terms within max_distance edits. Input only.
max_distanceinteger1Maximum Levenshtein distance for a fuzzy match. Lint accepts 0 to 8.
max_ageinteger0Age of the audience. From 1 to 17, adds the adult content check. Input only.

Lint accepts these modes: critical_infrastructure, automotive, education, law_enforcement, government, military, hr, justice, healthcare, finance, legal, and defense.

Built-in terms​

ModeTerms used when block_if is empty
critical_infrastructureexplosive, weapon, attack
automotivedisable airbags, bypass brakes, tamper
educationself-harm, suicide
law_enforcementdoxx, target
Every other modeexplosive, weapon, attack

How it works​

  1. The gateway joins the content of all request messages into one lowercase string.
  2. The control uses block_if if it is not empty, otherwise the built-in terms of mode.
  3. It matches each term at word boundaries. If none matched and fuzzy_matching is true, it compares each term with word windows, within max_distance.
  4. With max_age from 1 to 17, it also looks for the substrings alcohol, tobacco, gambling, explicit, pornograph, drug use, substance abuse, violence, gore, and profanity.
  5. No match: the input verdict is allow with safety.clean.
  6. A match: the input verdict is block or escalate, following action. The reason code is safety.age_inappropriate.<mode> for an age match, or safety.triggered.<mode> for a term.
  7. When the provider responds, the control matches the same terms against the response at word boundaries. Fuzzy and age checks do not run on the response.
  8. A response match gives an output verdict of block or escalate with safety.triggered.<mode>.

The input details contain mode, action, matched, fuzzy_matching, max_distance, max_age, and age_inappropriate. The output details contain mode, action, and matched.

Phase and verdicts​

ItemValue
Phaseinput and output
Request familiesChat Completions, Completions, Responses, Messages
Verdictsallow, block, escalate
Reason codessafety.clean, safety.triggered.<mode>, safety.age_inappropriate.<mode>

A request blocked on input gets HTTP 400 with error code content_policy_violation. An escalation returns HTTP 200 with content: null, finish_reason: content_filter, and a verdictan.escalation object with the escalation ID and status queued. With gateway 0.1.26 or later, a buffered escalation of a Responses request gets a response object with status: incomplete, the content_filter reason, and no output. A Messages request gets a message with no content and the refusal stop reason. On an escalation, the client does not get the model text. Gateway 0.1.25 and earlier record a buffered output escalation but still return the model text.

Limits and failure behavior​

  • Only the listed words match. A request that describes a dangerous action in other words passes. Do not rely on this control alone for safety.
  • The built-in lists are short, and target or attack match ordinary business text. Set block_if for production traffic.
  • block_if replaces the built-in terms. To keep one, add it to your list.
  • Only critical_infrastructure, automotive, education, and law_enforcement have their own terms. Other modes use the critical_infrastructure terms with a different reason code.
  • The max_age check is a substring match on ten fixed words, not a classifier. violence in a history question triggers it.
  • Output matching runs on streamed and buffered Chat Completions, Responses, and Messages responses. With gateway 0.1.26 or later, a buffered Responses block returns the same HTTP 400 error as Chat Completions. The input check runs on every listed family.
  • With stream: true and action: block, the gateway sends the text while it arrives. With gateway 0.1.26 or later, it holds back the text that can be the start of a term. Thus no part of a matched term gets to the client. Text before the term can already be with the client. See Output blocks in a stream.
  • With gateway 0.1.26 or later and stream: true, action: escalate examines streamed Chat Completions, Responses, and Messages output. The gateway holds back the text that can be the start of a term. Thus no part of a matched term gets to the client. Text before the term can already be with the client. On a match, the stream stops with the error event of the route. The event has the reason code, the verdict ESCALATE, and a verdictan.escalation object with status queued. See Output escalations in a stream.
  • When the control blocks or escalates a response, the request records the usage that the provider reports.
  • Set up an escalation route before you use action: escalate in production.

Next steps​