TL;DR: Counting what six guardrail tools ship is harder than it sounds, and I got one of them wrong on the first pass. The answers are 27, 0, 12 or 31, 14, 12 plus 15 plus 4, and one that cannot be counted at all. The numbers are not comparable, because the six do not agree on what a guardrail is or on who maintains the list, and two of them turn out to have the same architecture. The useful question is where the list lives.
Someone on my team said "we added guardrails" in a design review last month, and I nodded, and two weeks later I found out we had shipped a regex.
That is a review problem, and I could not fix it without a definition. So I tried to answer what looked like an easy question, which is how many checks each of the main guardrail tools gives you when you install it. It took three passes, and the interesting part is what went wrong in the first two.
Pass one, I read the docs, which was useless. Docs are written to describe capability, so they list things that are optional, paid, or on a roadmap, and I needed the set that lands on disk when you install. Pass two, I read one file per project and got a clean set of numbers, one of which was wrong by a factor of six because I had read the wrong file. Everything below is pass three, from source as of August 2026, with paths, including the correction.
The axis, and what I refused to count
The axis is narrow on purpose: how many distinct checks arrive when you install the thing, and who maintains that list. Not accuracy. Not latency. Not price.
I refused to count three things. Anything listed in docs but absent from the tree, because docs describe intent. Anything that wraps another vendor's detector, which I counted against that vendor. And anything behind a closed API, where there is no tree to read, which I report as unknown rather than guessing.
The six, by what ships
LLM Guard (Protect AI, open source, and archived). Worth knowing before you adopt it: the repo is read-only, last pushed 8 July 2026. In the 0.3.16 wheel, the two init.py export lists hold 15 input scanners and 22 output scanners, 37 entries. 10 names appear in both lists (BanCode, BanTopics, Toxicity, Regex and six more), so the distinct count is 27. Input-only: Anonymize, PromptInjection, Secrets, TokenLimit, InvisibleText. Output-only includes Bias, FactualConsistency, MaliciousURLs, NoRefusal, Relevance, URLReachability, Deanonymize, JSON, among others. Even at 27 this is the largest in-box inventory here, and I flag the 37-versus-27 gap because the summed number is the one usually quoted.
Two caveats that cost me a rewrite. The repo's main has 28. It carries an EmotionDetection scanner in both registries that appears in no released wheel, and since the repo was archived on 8 July 2026 it never will. I had 28 in an earlier draft of this post because I counted GitHub instead of the artifact, which is exactly the error this post is about, committed by the person writing it. And the 27 is not all LLM Guard's own work: Anonymize and Sensitive import Microsoft's Presidio, Secrets wraps Yelp's detect-secrets, and 23 of the modules pull in transformers to run third-party checkpoints. It is not exempt from the boundary problem below. It just draws the boundary inside the package instead of in a directory name.
Guardrails AI. Zero validators in the core package. guardrails/validators/init.py exports Validator, register_validator, ValidationResult, PassResult, FailResult and ErrorSpan, and stops. That is the architecture working as designed: validators live in Guardrails Hub as separately installed packages, and the core ships the base class and the registration machinery. The catalogue is real and large. It simply is not in the box, which means your dependency set and your check set become two lists that drift independently.
NVIDIA NeMo Guardrails. 31 rail directories under nemoguardrails/library/ (32 subdirectories, one of which is utils). By my reading, 12 are checks NVIDIA implements: attention, content_safety, context_bloat_detection, factchecking, gliner, hallucination, injection_detection, jailbreak_detection, regex, self_check, sensitive_data_detection, topic_safety. The other 19 are adapters to someone else, including activefence, autoalign, cleanlab, fiddler, patronusai, privateai, prompt_security, trend_micro, llama_guard and guardrails_ai. The boundary is fuzzy at three of the twelve. gliner wraps a third-party model (GLiNER is urchade's, not NVIDIA's), sensitive_data_detection/actions.py imports presidio_analyzer and presidio_anonymizer throughout, and factchecking leans on an external AlignScore server with a self-check fallback. Count all three as wrappers and the split is 9 own to 22 adapters. (hf_classifier is a wrapper as well, but it already sits in the 19.) Either way, 31 and 12 are both true and answer different questions.
Llama Guard 4 (Meta, 12B). Not a library. A model with a fixed taxonomy of 14 hazard categories, S1 through S14: Violent Crimes, Non-Violent Crimes, Sex-Related Crimes, Child Sexual Exploitation, Defamation, Specialized Advice, Privacy, Intellectual Property, Indiscriminate Weapons, Hate, Suicide and Self-Harm, Sexual Content, Elections, and Code Interpreter Abuse (text only). From Llama-Guard4/12B/MODEL_CARD.md in meta-llama/PurpleLlama. You run inference and get a label plus a category. There is no plugin surface.
Future AGI. This is the one I got wrong. Reading futureagi/ee/protect/helper.py gives four hosted metrics (toxicity, bias, privacy/PII, prompt injection) mapped in _UI_TO_METRIC, and I nearly published "four, the smallest inventory here". That file is the hosted metric map, not the inventory. agentcc-gateway/internal/guardrails/ holds 18 directories: external/ carries 15 vendor adapters as single Go files (lakera.go, presidio.go, llamaguard.go, bedrock.go, azure.go and ten more), futureagi/ is a client for Future AGI's hosted eval API, configured with whichever eval IDs you name (the Protect surface in futureagi/ee/protect/helper.py is the one that maps to four metrics), and of the remaining sixteen about twelve read as in-tree detectors (blocklist, contentmod, hallucination, injection, language, leakage, mcpsec, pii, secrets, sysprompt, toolperm, topic) with the rest closer to plumbing. The gateway README says 18 built-in scanners; I count about twelve that read as detectors, the same gap I flagged on LLM Guard's 37.
One thing the count does not show: those twelve are pattern matchers. hallucination/hallucination.go is a list of compiled regexes scoring matchCount * 0.2, and its own comment says to use Future AGI's hosted guardrail models for proper factuality verification. topic, injection, leakage, pii and contentmod are regex and phrase lists too. So twelve here is not the same kind of twelve as NeMo's, which includes LLM-backed self_check, factchecking and content_safety. Same architecture, own scanners plus a wall of third-party adapters, and a shallower own layer.
Lakera Guard. Hosted, closed source. There is no public tree to count, so I am not publishing a number for it. Whatever the docs list on the day you read them is the inventory, and it can change without a version bump you control. Someone else maintaining detector quality is a real benefit, and not being able to diff it is a real cost. Claiming I had verified a count would have been worse than leaving it blank.
The comparison table
| Tool | Checks shipped on install | Where the list lives | Verified in |
|---|---|---|---|
| LLM Guard | 27 distinct (37 export entries, 10 shared); several delegate underneath | In the pip package, archived 07/2026 |
llm_guard/{input,output}_scanners/__init__.py (0.3.16 wheel) |
| Guardrails AI | 0 in core | Guardrails Hub, installed per validator | guardrails/validators/__init__.py |
| NeMo Guardrails | 12 own plus 19 adapters (31 rails) | In the package, but 19 delegate | nemoguardrails/library/ |
| Llama Guard 4 | 14 hazard categories, fixed | In the model weights | Llama-Guard4/12B/MODEL_CARD.md |
| Future AGI | ~12 in-tree regex checks, 15 vendor adapters, 4 hosted metrics | Split: in the gateway tree, and vendor-side |
agentcc-gateway/internal/guardrails/, futureagi/ee/protect/helper.py
|
| Lakera Guard | not determined (no public tree to read) | Vendor-side, changes without your release | closed source |
Check the LLM Guard number yourself
The central count takes about ten seconds to reproduce, which is the point of publishing the paths:
# 0.3.16 is the last release (repo archived 8 July 2026). Its requires_python is
# >=3.10,<3.13, so this pinned download only works on 3.10, 3.11 or 3.12. On 3.13+
# pip errors with "No matching distribution found" rather than quietly falling back.
# The repo's main branch has one more scanner, EmotionDetection, that shipped in no
# release at all: main says 28, the artifact says 27.
pip download llm-guard==0.3.16 --no-deps -d /tmp/lg && cd /tmp/lg && unzip -o -q llm_guard*.whl
python - <<'PY'
import ast
def names(p):
tree = ast.parse(open(p).read())
for node in ast.walk(tree):
if isinstance(node, ast.Assign) and getattr(node.targets[0], "id", "") == "__all__":
return {e.value for e in node.value.elts} - {"get_scanner_by_name"}
return set()
i = names("llm_guard/input_scanners/__init__.py")
o = names("llm_guard/output_scanners/__init__.py")
print(len(i), len(o), len(i & o), len(i | o)) # 15 22 10 27
PY
Three things the count tells you
Zero can be the honest answer. Guardrails AI shipping no validators in core is the design behaving correctly. The cost is that "which checks are we running" stops being answerable from your lockfile. If you go that route, write down on day one where the list lives and who may change it.
A fixed taxonomy buys a property the extensible ones cannot. Llama Guard is the only one of the six in this category: 14 categories, fixed in the weights, no plugin surface at all, so the behaviour you test in staging is the behaviour you get in production. It will not cover a case outside its taxonomy, and it will not surprise you with one either. Whether that trade is right depends on whether your risk surface is narrow enough to fit inside it, and for most teams it is not. Note that this is the property I nearly credited to Future AGI on the strength of its four hosted metrics, before its gateway tree showed me fifteen vendor adapters and a plugin registry.
A big number can mean the vendor did less work, and two of these six are the same shape. NeMo's 31 is the obvious case, since 19 of those directories are adapters. Future AGI's gateway is the same architecture: roughly a dozen in-tree scanners wrapped around fifteen vendor adapters. An adapter layer is useful, and for either project it is a fast way to put several vendors' detectors behind one config, but it is an integration achievement rather than a detection one, and it means the two projects I would have described as opposites are structurally siblings. The same trap sits inside LLM Guard's own headline, where 37 export entries turn out to be 27 detectors, and inside my own first draft, which said 28 because I counted a branch instead of a release. Every number on this page needed a second look before it meant anything, which is the actual finding.
What this changes about the review conversation
"Did we add guardrails" is not answerable. Three questions that are:
- Which named checks run, on input, on output, per route?
- Where is that list defined, and does a change to it show up in our diff?
- What happens on the check's own failure path: does the request proceed, or stop?
Ask them per tool call, not per app, which is where this bites for anyone shipping agents: an agent that can hit a payments API and a search API through the same runtime does not want one guardrail config, and "which checks run on this route" is a different answer for the two. Question 2 is what separated the six tools above, and it is the one nobody asks in a design review. Question 3 I have left open. Answering it properly means reading six exception paths and reasoning about timeouts under load, and I would rather ship five counts I verified than six behaviours I inferred. It is the better question, and I have not earned an answer to it yet.
Where I'd push back on this
Counting checks is a shallow axis and it implicitly rewards quantity, which is the objection I would lead with if I were reading this. Fair. One well-tuned prompt-injection detector that fits your traffic beats twenty-eight that do not, and nothing in these numbers says whether a given detector works on your data. I picked this axis because it is verifiable from source and because it exposes the packaging differences that make the usual vendor comparison meaningless.
An objection I would also accept: leaving Lakera's cell empty while everyone else gets a number reads as a penalty. A closed-source tool is not worse for being closed, and plenty of teams are glad to have a vendor own detector quality. The empty cell records what I could verify, not a judgement about the product.
An objection I would not accept: that the Guardrails AI zero is a cheap shot. It is what the file exports. A reader who installs the core package expecting validators gets a base class, and that is better learned before the sprint than during it.
Source references checked August 2026. These trees move. The paths are in the table so you can re-run the counts rather than trust mine.

Top comments (0)