detectors: read Llama Guard verdicts in shields - #2109
Closed
Yigtwxx wants to merge 1 commit into
Closed
Conversation
Up and Down prefix-match a list of likely words, which suits a firewall whose output vocabulary is unknown. Llama Guard publishes an exact contract instead - a bare verdict on the first line, then the violated categories - and matching that loosely costs three things: a leading newline defeats startswith, so a real block scores as shields-down; prose that opens with a verdict word is read as a verdict; and the hazard category, the most informative part of the response, is dropped. LlamaGuardUp reads the verdict line exactly and records the cited category codes in the attempt notes. LlamaGuardDown is its inverse, for checking known-clean probes for guard false positives. Codes are stored as cited rather than resolved to hazard names, because the code-to-hazard mapping differs between Llama Guard releases. Co-authored-by: Claude Signed-off-by: Yigtwxx <yigiterdogan023@gmail.com>
Author
|
Withdrawing as promised. @immu4989 has a branch for this and raised the design questions on #1191 first, so the issue is theirs. @immu4989, two findings from my branch in case they save you time, take them or leave them:
Good luck with the PR. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
shields.LlamaGuardUpandshields.LlamaGuardDown, detectors for guard models that answer with a Llama Guard style verdict, and records the hazard category codes those models cite in the attempt notes.Closes #1191. Shape taken from @leondz's guidance on that issue: guard models with a binary output belong in
shields, the category outcome goes in notes, and no endpoint vendor is named anywhere.Why the existing Up / Down are not enough
UpandDownprefix-match a list of likely words. That is the right design for a firewall whose output vocabulary is unknown, which is what they were built for. Llama Guard publishes an exact contract instead - a bare verdict on the first line, then a comma-separated list of violated categories - and matching that loosely costs three things.A real block can score as shields-down.
matchtype: "startswith"runs on the raw text, and guard models routinely emit a leading newline. Onmainat 3845757:That is a hit, meaning the guardrail let the content through, for a response in which the guard blocked it. For a guard detector, errors in this direction are the expensive ones.
Prose reads as a verdict.
Downscores"safe to proceed"and"no idea"as safe verdicts, because they begin withsafeandno.The hazard category is discarded.
S1, orS1,S10, is the most informative part of a Llama Guard response and is dropped today.What the new detectors do
LlamaGuardUpreads the verdict line exactly: the first non-blank line, tolerant of surrounding whitespace and of the bold markers some wrapper stacks add, holding the verdict token and optionally the hazard codes and nothing else. A line carrying other words is prose and yields no verdict. Codes are read from that line and the one after it, deduplicated, upper cased, and written toattempt.notes["llamaguard_hazard_categories"]as one entry per output.LlamaGuardDownis its inverse over the same contract, for testing known-clean probes for false positives from the guard - the reasonDownexists alongsideUp.Codes are stored as cited rather than resolved to hazard names on purpose.
S3means different things in Llama Guard 2 and Llama Guard 3, and Llama Guard 1 emitsO-codes; a detector cannot know which release the target runs, so a baked-in map would mislabel silently for some targets. The raw codes round-trip losslessly and are trivially mapped downstream.Polarity follows the rest of the module. A fired guardrail is a pass for
LlamaGuardUp, a quiet one is a pass forLlamaGuardDown, and a response holding no verdict is a hit for both - exactly as an unmatched response already is forUpandDown. That last case is also logged at debug level with a count, so an all-hits run against a non-guard target is diagnosable.The verdict tokens are configurable through
DEFAULT_PARAMS, so guard models that share the output shape but not the vocabulary can be scored without a new class.Two files touched, no new dependencies, no network access, no model downloads.
Why this is not a duplicate
Checked before opening, per
AGENTS.md:The nearest open PR is #1893, which adds a
stripoption toStringDetector. It is complementary rather than overlapping: astripoption would fix the leading-whitespace symptom above, but not prose being read as a verdict and not the discarded hazard codes, which are what #1191 asks for. Nothing here touchesStringDetector, so the two can land in either order.Verification
python -m garak -t test.Blank -S probes.test.Blank -d shields.LlamaGuardUpand the same withshields.LlamaGuardDown- both load, score, and report.python -m garak --plugin_info detectors.shields.LlamaGuardUprenders the description, params and descs.python -m pytest tests/-5737 passed, 101 skipped in 2371.61s, on Windows with Python 3.11.python -m pytest tests/detectors/test_detectors_shields.py -q-35 passedpython -m pytest tests/detectors/test_detectors.py -q -k shields-16 passed,python -m pytest tests/plugins/test_plugins.py -q -k shields-8 passed,python -m pytest tests/test_docs.py -q-682 passedpython -m black --config pyproject.toml --check garak/detectors/shields.py tests/detectors/test_detectors_shields.py-2 files would be left unchanged"safe to proceed","This content is unsafe.") yields no verdict rather than a false reading; outputs with no text relayNone; scoring the same attempt twice rebuilds the notes rather than accumulating them.docs/source/detectors/shields.rstis a bareautomodule, so it picks both classes up with no change.Honest limit on that verification: a meaningful end-to-end score needs a real Llama Guard target, which is not available offline or in CI, and which the neutrality point on the issue means should not be hard-coded as an example. The
test.Blankrun exercises plugin load, config resolution, thedetect()contract, notes handling and evaluator integration, but says nothing about classification quality. The unit tests are the substantive correctness evidence. I could not run the Sphinx job locally either -docs/source/_ext/garak_ext.pyneeds Python 3.12 for its f-strings and my venv is 3.11 - so I parsed the three new docstrings with docutils in strict mode instead, which reports no warnings.garak/resources/plugin_cache.jsonis deliberately not included; the workflow onmainregenerates it.AI assistance
This change was developed with AI assistance. I reviewed every changed line, ran the commands listed above myself, and can speak to the parsing rules and the polarity choices.