Search by

drupal / ai_recipe_validations_image_safety

Drupal-Infrastructure

Validates image uploads to Media: Image with a vision-capable AI provider. Blocks Restricted content (full nudity, explicit sexual content, gore, graphic violence, graphic medical imagery) and accepts Moderate content (partial nudity, sexually suggestive content, mild violence) with a warning. Uses

Package info

git.drupalcode.org/project/ai_recipe_validations_image_safety.git

Homepage

Type:drupal-recipe

pkg:composer/drupal/ai_recipe_validations_image_safety

Statistics

Installs: 767

Dependents: 0

Suggesters: 0

1.1.0 2026-11-02 15:15 UTC

This package is auto-updated.

Last update: 2026-11-02 15:50:30 UTC


README

Classifies every image uploaded to the Media: Image bundle into one of three Flickr-style safety tiers using AI Validations and any vision-capable AI provider:

  • Safe — accepted silently.
  • Moderate (partial nudity, sexually suggestive content, mild violence) — accepted, but the uploader sees a warning.
  • Restricted (full nudity, explicit sexual content, gore, graphic violence, graphic medical imagery) — blocked with a validation error.

Requirements

  • Drupal 10.3+ or 11
  • AI module 1.4 or newer (provides the verifySetupAi config action and the ai_provider_configuration form element)
  • AI Validations (standalone) 1.4 or newer — provides the opt-in XSAFE/XWARN/XBLOCK warning tier that this recipe relies on (see #3606131) and resolves the shipped rule's empty provider to the site default both in the rule form ("Default" option) and at validation time (see #3586397).
  • Field Validation 3.0 or newer — 3.0.0 is a stable release, so the recipe installs on projects with the default minimum-stability: stable (Drupal CMS, Project Browser) without any extra step.
  • A configured default provider for the chat_with_image_vision operation type (OpenAI GPT-4o, Anthropic Claude 3.5 Sonnet, Google Gemini 1.5 Pro, etc.) at /admin/config/ai/settings → Default Providers

Note: the AI module still bundles a deprecated copy of ai_validations. When the standalone package is installed, Drupal's extension discovery automatically prefers it (shallower path wins) — no uninstall/reinstall is needed on existing sites, just a cache rebuild. Without the standalone package, the deprecated copy treats the recipe's empty provider as an error and blocks every upload with "No AI provider specified to do validation", and it does not know the warning tier at all.

Apply

Set up a vision-capable provider before applying. On a site where the AI module is not yet configured, the recipe's preflight check will abort the apply (see "Recovering from a failed apply" below).

composer require drupal/ai_provider_openai drupal/ai_recipe_validations_image_safety
drush en ai ai_provider_openai key -y

(Substitute ai_provider_openai with the provider module of your choice — ai_provider_anthropic, ai_provider_google_gemini, etc.)

Then, in the admin UI:

  1. Add your API key at /admin/config/system/keys.
  2. Configure the provider at /admin/config/ai/providers.
  3. Set a default model for the chat_with_image_vision operation type at /admin/config/ai/settings → Default Providers.

Finally, apply the recipe:

php core/scripts/drupal recipe ../recipes/ai_recipe_validations_image_safety
drush cache:rebuild

Recovering from a failed apply

If the apply aborts with "The operation type 'chat_with_image_vision' does not have a default model", the site was rolled back to its pre-apply state — modules reported as installed earlier in the output were reverted as part of the rollback. Recipe application is atomic, and on a site without a configured provider the check cannot pass, because configuring a default model itself requires the AI module. Follow the setup steps above (enable the modules, configure a default model), then re-run the apply.

Upgrading an existing site

The recipe creates the media_image rule set only if it does not exist yet (createIfNotExists). Re-applying a newer recipe version on a site that already has the rule set does not change the existing rule. Either edit the rule by hand as described under "Adjusting the tiers" (set the prompt's === DECISION === and === OUTPUT FORMAT === blocks to the XSAFE/XWARN/XBLOCK tokens and fill in the Warning message), or delete the rule set at Structure → Field Validation and re-apply the recipe.

What it does

  • Applies core's image_media_type recipe first (creates the Media: Image bundle if it does not exist).
  • Installs ai, ai_validations, and field_validation.
  • Creates a field validation rule set media_image attached to the Media: Image bundle's field_media_image field.
  • Runs the AI image constraint on the target_id column in entity validation mode — catches UI uploads, JSON:API posts, and programmatic saves alike.
  • Sends the classification prompt to the vision model and acts on the single token it returns:
    • XSAFE — the image is saved, nothing is shown.
    • XWARN — the image is saved and the configured warning message is shown to the uploader (via the messenger) after the save. The warning is also written to the ai_validations logger channel, so headless flows (JSON:API, programmatic saves) keep an audit trail; a warning never blocks those flows.
    • XBLOCK — the save is rejected with the configured error message.
    • Any other answer, or an image the model cannot classify with confidence, is treated as Restricted and blocked (fail closed).
  • Tier boundaries are judged by the model, so an image that sits on the edge of two tiers (for example a classical painting with partially covered nudity) can land in either tier on different uploads. The prompt tells the model to pick the stricter tier whenever two apply, so borderline images err towards a warning or a block, never towards a silent pass.

Adjusting the tiers

The prompt decides which tier an image falls into, and the two messages decide what the uploader sees. To change the defaults:

  1. Go to Structure → Field Validation → AI image safety (Media: Image)
  2. Edit the Block restricted imagery, warn on moderate rule
  3. Change the === DECISION === block of the prompt as needed:
    • To block Moderate content as well, change its line to:
      - If the image is MODERATE, respond with: XBLOCK
      
    • To accept Moderate content silently (no warning), change its line to:
      - If the image is MODERATE, respond with: XSAFE
      
    • To tighten or loosen a tier, move the relevant bullets between the SAFE, MODERATE and RESTRICTED sections of === SAFETY LEVELS ===.
  4. Edit Message (shown when an image is blocked) and Warning message (shown when a Moderate image is accepted) to suit your site.

Returning to plain blocking (XTRUE/XFALSE)

The warning tier is opt-in in AI Validations: it is active only while the Warning message field is non-empty. To go back to the binary protocol you must change both of the following, otherwise every upload is blocked:

  1. Empty the Warning message field. With it empty, AI Validations expects the model to answer XTRUE (pass) or XFALSE (fail); an XSAFE answer is then an unrecognised token and fails validation.
  2. Rewrite the === DECISION === and === OUTPUT FORMAT === blocks of the prompt to use XTRUE / XFALSE, for example:

    === DECISION ===
    - If the image is SAFE, respond with: XTRUE
    - If the image is MODERATE or RESTRICTED, respond with: XFALSE
    
    === OUTPUT FORMAT ===
    Respond with exactly one token — either XTRUE or XFALSE — and nothing else.
    No explanation. No punctuation. No surrounding whitespace.
    

Conversely, if you only fill in a Warning message without switching the prompt to the three tokens, an XTRUE answer still passes but an XFALSE answer blocks, so no warning is ever shown.

Cost note

Every image upload triggers one vision-model API call. Bulk migrations, imports, or high-volume user upload flows will incur proportional cost. Test with your provider's pricing before rolling out to production.

Issue queue

Bugs and feature requests: https://www.drupal.org/project/issues/ai_recipe_validations_image_safety