Skip to content

feat(validators): add content_safety wrapper over uli_slur_match and profanity_free - #164

Merged
Prajna1999 merged 5 commits into
mainfrom
feat/content-safety-validator
Oct 15, 2026
Merged

Prajna1999 merged 5 commits into
mainfrom
feat/content-safety-validator

Conversation

@Prajna1999

@Prajna1999 Prajna1999 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Target issue: "Add a common validator for Content Safety" (unified wrapper over profanity_free and uli_slur_match).

Adds a content_safety validator type that runs both checks as one validator, so callers don't have to configure and order two separate validators.

Changes

  • ContentSafety validator (core/validators/content_safety.py): runs LexicalSlur first, then scores the redacted text with alt-profanity-check (the model behind ProfanityFree) against a threshold, and returns a single pass/fail with combined errors.
  • ContentSafetySafetyValidatorConfig: type: "content_safety", slur_severity (low / medium / high / all, default all), profanity_threshold (0–1, default 0.9), and the usual on_fail.
  • ValidatorType.ContentSafety added and the config added to ValidatorConfigItem, so GET /guardrails/ lists it automatically.
  • No migration (validator_config.type is a string column). No changes needed in ai-platform: it only passes validator_config_id.

Design decisions

  • Profanity cutoff is 0.9, not ProfanityFree's fixed 0.5. The model is English-only and treats ho as the slur "hoe" (0.883) and tu as 0.535, so namaste, kaise ho, kya ho raha hai and tu kahan hai were blanked. English profanity still scores 0.95–1.0. Trade-off: screw you (0.893) now passes. Lower profanity_threshold per config if needed.
  • Slur check runs first. The profanity check has no partial fix (fix_value=""). Redacting listed slurs first keeps the rest of the message when a slur is the only problem.
  • No languages field. LexicalSlur stores languages but never uses it (the CSV has no language column), so exposing it would be a setting that does nothing.
  • Fixed error messages. ProfanityFree's message embeds the full input. Inside the wrapper it sees lowercased, redacted text, so rephrase_query_on_fail's str.replace(value, "[REDACTED]") can't scrub it. The wrapper uses "Text contains profanity" instead.
  • Inner validators use OnFailAction.NOOP. Only the wrapper's on_fail applies.

Ordering results (real slur list + real profanity model)

Input content_safety (slur-first) Profanity-first
you are a b**ch you are a [REDACTED_SLUR] ""
That guy is a faggot, ignore him that guy is a [REDACTED_SLUR], ignore him ""
What the fuck is this, you bitch what the [REDACTED_SLUR] is this, you [REDACTED_SLUR] ""
you b***od, fuck off you [REDACTED_SLUR], [REDACTED_SLUR] off ""
Woh ladki r**di hai woh ladki [REDACTED_SLUR] hai same
You are a r**di and a piece of shit "" (shit isn't on the slur list) ""
this is fucking ridiculous "" ""
Clean English / Hindi / Hinglish pass pass

Across 21 sample inputs, slur-first was never worse than profanity-first and kept the message in 5 of them.

Checklist

  • Ran fastapi run --reload app/main.py or docker compose up in the repository root and test.
  • If you've fixed a bug or added code that is tested and has test cases.

Notes

  • Tests: 47 new tests in tests/validators/test_content_safety.py, including parametrised real-model ordering cases. They were run locally with --noconftest because the test DB wasn't available. They don't touch the DB; CI runs the full suite.
  • Known limitation: alt-profanity-check is an English model and doesn't detect Hindi slurs; only the slur list does. The 0.9 threshold fixes the known Hinglish false positives, but ho sits close to it, so a longer Hinglish message containing ho plus another mildly weighted word could still cross it.
  • profanity_check is imported directly. It's installed through guardrails-ai-profanity-free, not listed in pyproject.toml by itself.
  • Existing LexicalSlur behaviour, unchanged here: a redaction lowercases the whole text, and duplicate CSV labels repeat in the error message (e.g. randi, randi).

🤖 Generated with Claude Code

…profanity_free

Single validator that runs the lexical slur check, then ProfanityFree on the
redacted text. Slur-first keeps the rest of the message when the only problem
is a listed slur; ProfanityFree has no partial fix and would blank it.

- Config exposes only slur_severity; LexicalSlur ignores `languages`.
- Fixed error messages so user text isn't echoed into validator logs or
  rephrase responses (ProfanityFree's message embeds the full input).
- Inner validators use NOOP on_fail; only the wrapper's on_fail applies.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
@coderabbitai

coderabbitai Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration
  • Configuration used: Repository: ProjectTech4DevAI/kaapi-guardrails/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 71fa1593-7f09-4d44-b6c5-29f2a6966961
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Prajna1999 and others added 2 commits October 14, 2026 20:47
ProfanityFree fails anything the alt-profanity-check model scores at 0.5 or
more. The model is English-only and weights "ho" (as in "kaise ho") at 0.883
and "tu" at 0.535, so common Hinglish was blanked.

Score with predict_prob directly and fail at profanity_threshold (default
0.9, range 0-1). English profanity still scores 0.95-1.0.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Validator,
register_validator,
)
from profanity_check import predict_prob

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we are importing this but package (alt-profanity-check) is not in our pyproject.toml.It only gets installed because guardrails-ai-profanity-free depends on it, with no version pin. Result: lock file holds two versions, each with different trained model, so profanity scores vary by Python version.

add "alt-profanity-check==1.8.0" in backend/pyproject.toml.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The concern is valid but the suggested pin would break the production build the 1.8.0 version requires python 3.11 while the production image runs at python 3.10. Checked the claim if two versions varry, the scores would but the score holds.

@@ -0,0 +1,66 @@
from collections.abc import Callable

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should add this new validator in backend/app/core/validators/validators.json so it shows all 11 validators. and update readme as well if not already

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

Prajna1999 and others added 2 commits October 15, 2026 09:02
Add content_safety to validators.json (source: local) and document it in the
validators README (supported list, details, stage guidance, related files)
and API_USAGE validator types.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
It was the only ValidatorType missing from the manifest.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
@Prajna1999
Prajna1999 merged commit 7fb69e8 into main Oct 15, 2026
2 checks passed
@Prajna1999
Prajna1999 deleted the feat/content-safety-validator branch October 15, 2026 03:36
@github-project-automation github-project-automation Bot moved this to To Do in Kaapi-dev Oct 15, 2026
@AkhileshNegi AkhileshNegi moved this from To Do to Closed in Kaapi-dev Oct 15, 2026
@AkhileshNegi AkhileshNegi removed this from Kaapi-dev Oct 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants