How it works
Defenses
Attack Patterns MCP Attack Atlas What we catch Hardening manual OWASP LLM Top 10 MITRE ATLAS
Learn
Encyclopedia (new) Agent Security 101 Blog Reports CVP runs Thesis
Resources
Docs GitHub Action (live) vs Lakera vs Promptfoo Team
Theme
Migration guide

Moving from LLM Guard to Sunglasses

Every Sunglasses example on this page was run against the 0.6.2 wheel on 2026-09-28
Read this first

The LLM Guard repository is archived on GitHub. Its README says the project and its models are no longer maintained. Sunglasses is a different kind of tool. It matches known attack wording and shapes with rules that run on your machine. Text scanning needs no model download. Some LLM Guard scanners have a direct replacement here and some have none. This page says which, so you can plan the gap instead of finding it later.

What maps and what does not

LLM Guard is a set of scanners you pick one by one. Sunglasses is one engine with a rule set you can extend. Read the right column before you remove anything.

sunglasses / migrate / llm-guard
PromptInjectionReplaced by the built in rules. LLM Guard asks a classifier model for a score. Sunglasses matches wording and shapes it has rules for and returns a decision with the rule ids that fired. A paraphrase no rule covers can pass, so keep your other defences.
SecretsReplaced by the built in credential rules. The AWS documentation example key is refused with block at critical. Sunglasses refuses the text and does not redact it.
BanSubstrings, Regex, BanCompetitorsReplaced by your own rules passed as extra_patterns. See the section below.
TokenLimitNo token counting. max_scan_bytes caps how much text is scanned. A capped scan says so with inspection_complete set to False.
InvisibleTextNo equivalent. Sunglasses never removes characters from your text.
Anonymize and DeanonymizeNo equivalent. There is no personal data vault and nothing is rewritten.
Toxicity, Sentiment, Bias, BanTopics, Language, Gibberish, CodeNo equivalent. These are classifier jobs and Sunglasses does not run classifier models on text.
Output scanners such as Relevance, FactualConsistency, NoRefusal and JSONNo equivalent. You can pass a model reply through the same scan to catch a credential in it, shown below.

The call, before and after

This is the prompt check from the LLM Guard quickstart, shortened.

python · before
from llm_guard import scan_prompt
from llm_guard.input_scanners import PromptInjection, Secrets

scanners = [PromptInjection(), Secrets()]
sanitized_prompt, results_valid, results_score = scan_prompt(scanners, prompt)
if not all(results_valid.values()):
    raise ValueError(f"refused, scores {results_score}")

The same check in Sunglasses. Build the engine once and reuse it, because building it compiles the whole rule set.

python · after
from sunglasses.engine import SunglassesEngine

engine = SunglassesEngine()

def check(text, channel="message"):
    result = engine.scan(text, channel=channel)
    if not result.inspection_complete:
        raise ValueError("not fully scanned, so not checked")
    if result.decision in ("block", "quarantine"):
        ids = [f["id"] for f in result.findings]
        raise ValueError(f"refused ({result.severity}) by {ids}")
    return text
sunglasses / migrate / results
sanitized_prompt

Nothing to replace it with. Sunglasses does not rewrite text, so you either pass the original on or refuse it.

results_valid

result.decision is one of allow, allow_redacted, quarantine or block. High and critical findings block. Medium findings quarantine. Low findings return allow_redacted, which lets the text through unchanged.

results_score

result.severity and result.findings, where each finding carries the rule id, its name and the matched text. There is no probability, because a rule either matched or it did not.

The channel

Tell the engine where the text came from, for example message, file or web_content. A misspelled channel raises ValueError instead of scanning against no rules.

Your own banned words and patterns

BanSubstrings and Regex become rules you pass to the engine. Keep words and regexes in separate rules. In a rule that has both, the keyword only nominates the rule and the regex decides.

python
from sunglasses.engine import SunglassesEngine

banned_words = {
    "id": "ACME-001",
    "name": "Internal project names",
    "category": "custom",
    "severity": "medium",
    "channel": ["message", "web_content"],
    "keywords": ["project bluebird", "acme rival corp"],
    "description": "Names we do not send to a model.",
}

ticket_ids = {
    "id": "ACME-002",
    "name": "Internal ticket ids",
    "category": "custom",
    "severity": "high",
    "channel": ["message", "web_content"],
    "regex": [r"\bACME-\d{4,}\b"],
    "description": "Ticket ids from our tracker.",
}

engine = SunglassesEngine(extra_patterns=[banned_words, ticket_ids])

for text in ["Ask Acme Rival Corp for a quote", "See ACME-20931 for details", "hello"]:
    result = engine.scan(text, channel="message")
    print(result.decision, [f["id"] for f in result.findings])
output on 0.6.2
quarantine ['ACME-001']
block ['ACME-002']
allow []

Keywords ignore case. A keyword must not run on into a longer word, so project bluebird does not match project bluebirds, but the start of a match is not checked. Very common single words on the engine's own denylist, such as token, secret and config, never fire as a keyword on their own. Put a word like that in a regex rule instead. Regexes are compiled case insensitive.

Checking a model reply

There is no separate output API. Pass the reply through the same check and the credential rules still apply, which covers the most common output leak. Relevance, refusals and factual checks have no replacement here.

python
reply = "Sure. The key is AKIAIOSFODNN7EXAMPLE"
check(reply)   # ValueError, refused (critical)

Outside Python

If you ran the LLM Guard API as a service, the closest pieces here are the command line and the MCP server. There is no HTTP server.

bash
pip install sunglasses
sunglasses scan "text to check" --channel web_content --json   # exit 1 on a finding, 0 when clean
python -m sunglasses.mcp                                        # MCP server over stdio

What this page does not claim

sunglasses / migrate / not-claimed
No comparison

Nothing here measures how well either tool detects anything. This is a map of calls and features.

The before code

It is taken from the LLM Guard quickstart and was not run for this page. Every Sunglasses example was.

Coverage

Rules catch what they describe. Read what Sunglasses catches and what it does not before you rely on it.