Repo: github.com/tarunprajapati88/GuardrailOps
The problem: guardrails that protect but don't observe
Picture a companion AI or mental-health chatbot at 2 AM. A user types something like "I don't want to be here anymore." A decent guardrail will catch that and block the raw response from reaching the user unfiltered.
But then what? In most setups — nothing. The block happens silently, in application memory, and disappears. Nobody on the team knows a user was in crisis. Nobody knows if the same user is hammering the system with jailbreak attempts five minutes later. The guardrail protected the interaction, but nobody was watching it.
That distinction — protection vs. observability — is the whole reason GuardrailOps exists. Existing tools like NeMo Guardrails, Guardrails AI, and @openai/guardrails all answer "is this text bad?" None of them answer "when your AI fails a vulnerable user at 3 AM, how do you know — and what do you do about it?"
What GuardrailOps actually does
GuardrailOps is a drop-in wrapper around your existing LLM client (OpenAI, Ollama, vLLM, Groq, DeepSeek — anything OpenAI-compatible). It sits between your app and the model:
import { wrapWithGuardrailOps } from "guardrailops";
import { llmClient } from "./llm-client";
const client = wrapWithGuardrailOps(llmClient, {
domains: ["mental-health", "abuse", "jailbreak", "illegal"],
classifier: "llama-guard", // Meta Llama Guard 3 via Ollama
llamaGuard: {
endpoint: "http://localhost:11434",
model: "llama-guard3:1b",
},
otel: {
serviceName: "my-chatbot",
exporterEndpoint: "http://localhost:4318",
},
});
const response = await client.chat.completions.create({
model: "my-llm-model",
messages: [{ role: "user", content: userMessage }],
});
That's it. Every call now gets classified before it reaches the model. Safe traffic flows through untouched. Anything flagged gets blocked with a safe fallback, and — this is the part other guardrail libraries skip — every decision gets emitted as telemetry.
Architecture
Here's the full request path, from user message to SRE getting paged:
Six moving pieces, none of them optional:
- The SDK proxy wraps your existing client — your application code barely changes.
- A two-layer classifier — Llama Guard 3 as the primary model, plus a sub-millisecond regex pre-filter as a fast-path net for obvious jailbreak/persona-hijack attempts.
- A stateless severity scorer decides BLOCK vs ALLOW and whether the event is serious enough to page someone.
- An OTel span emitter ships every decision — not the raw message — to an OpenTelemetry Collector.
- The Collector scrubs PII before anything touches SigNoz's ClickHouse store.
- SigNoz's Alert Engine and MCP Server turn raw trace data into Slack pages and natural-language triage.
The two-layer classifier
| Layer | Engine | Covers | Latency | Cost |
|---|---|---|---|---|
| 1 — Primary | Meta Llama Guard 3 (1B), local via Ollama (or NVIDIA NIM / OpenAI Moderation as swappable backends) | 13 MLCommons safety categories (S1–S13) | ~480ms | $0 — nothing leaves your infra |
| 2 — Fast path | Heuristic regex pre-filter | DAN prompts, persona hijacks, base64-encoded jailbreak attempts | <1ms | $0 |
The privacy/latency tradeoff is explicit and intentional: running Llama Guard 3 locally costs roughly 10x the latency of a cloud moderation API call, in exchange for zero data leaving your infrastructure and zero per-request fees. For a mental-health or companion-AI use case, that tradeoff is usually worth it.
Llama Guard 3's S1–S13 taxonomy gets mapped down to five developer-friendly domains:
| MLCommons category | GuardrailOps domain | Default action |
|---|---|---|
| S11 (Suicide & Self-Harm) | mental-health |
BLOCK + 988 Lifeline + page SRE, +0 threat points |
| S5, S7, S10, S12 (Defamation, Privacy, Hate, Sexual) | abuse |
BLOCK + flag user (+10 pts) |
| S1, S2, S3, S4, S9 (Violent, Non-Violent, Sex, CSAM, CBRN) | illegal |
BLOCK + flag + push alert (+25 pts) |
| Layer-2 regex hits | jailbreak |
BLOCK + flag + push alert (+15 pts) |
| S6, S8, S13 (Specialized advice, IP, Elections) | off-topic |
BLOCK (+5 pts) |
That mental-health row is the core design decision in the whole project: crisis users are protected, not penalized. They get zero threat points added to their account, so a genuine crisis never escalates someone into a RESTRICTED or BLOCKED state. The Slack alert exists to bring a human into the loop, not to punish the user.
Seeing it work: the demo app
The repo ships a MindBot demo chatbot with preset buttons for each scenario — Distress, Crisis, Abuse, Jailbreak — so you can watch the pipeline react without writing a test harness.
Each blocked response shows exactly what fired — domain, category, and which engine caught it, down to the millisecond. In this run, a "give me instructions to synthesize meth" prompt was caught by Llama Guard 3 in 6ms and tagged illegal / violent_crimes, and the Slack SRE alert badge confirms the page went out — all without the raw prompt ever leaving the app.
Where it lands: SigNoz
Every one of those blocked events becomes a standard OpenTelemetry GenAI span with custom guardrail.* attributes:
gen_ai.system = "guardrailops"
gen_ai.request.model = "llama-guard3:1b"
guardrail.triggered = true
guardrail.action = "BLOCKED"
guardrail.domain = "illegal"
guardrail.category = "violent_crimes"
guardrail.crisis.severity = "CRITICAL"
guardrail.classifier = "llama-guard"
guardrail.classifier.latency_ms = 480
guardrail.user.id = "usr_sha256_e3b0c442"
guardrail.session_id = "sess_demo_100"
Those spans feed a pre-built SigNoz dashboard (ships as dashboard.json, one-click import) that turns raw blocks into something an SRE can actually read at a glance:
At a glance: which threat domains are spiking, which classifier engine is catching what, the breakdown of violation categories (suicide/self-harm, prompt injection, violent crimes), and — critically — a repeat-offender leaderboard by hashed user ID. That last panel is what turns "we got jailbreak attempts today" into "this specific user has hit us 14 times."
User identifiers are SHA-256 hashed before they're ever exported, and the raw prompt/response text is stripped at the OTel Collector layer — SigNoz only ever sees the metadata, never the conversation.
From block to page: Slack
When something crosses a severity threshold, SigNoz's Alert Engine fires a webhook to a relay service, which posts a Block Kit card straight into #alerts:
On-call SREs don't have to leave Slack to investigate, either — a Slack MCP bot lets you query live SigNoz trace data conversationally:
@GuardrailOpsBot show trace for session sess_ms1umloe_7fw5ma pulls the session's classification history straight from ClickHouse, no dashboard click-through required. @GuardrailOpsBot summary of today gives a fleet-wide block-rate and incident count for the day.
What it deliberately doesn't store
Given the domains involved (mental health, abuse, illegal content), the privacy story matters as much as the safety story:
- ❌ The user's actual message text
- ❌ The LLM's response content
- ❌ Chat history or conversation context
- ❌ Raw user identifiers (email, name, IP)
GuardrailOps also ships GDPR-oriented APIs out of the box — clearUser() for right-to-be-forgotten requests, and setThreatTTL() so threat scores decay automatically rather than persisting indefinitely.
Where this fits
If you're building a companion AI, a mental-health support bot, or really any consumer-facing chatbot handling sensitive input, the pitch is simple: your guardrail should not be a black box that silently swallows the worst moments in a user's day. GuardrailOps is a proof of concept for making that visible — who triggered what, when, and whether it was a bad actor or someone who needed help.
It's a solo hackathon build (developed with AI-assisted coding, disclosed per the SigNoz "Agents of SigNoz" hackathon rules — architecture and safety logic are original work), not a clinically validated medical tool. It's not a replacement for professional mental health care. But as a pattern for wiring safety decisions into observability infrastructure that already exists in most teams' stacks, it's a starting point worth building on.
Full source, setup instructions, and the complete OTel span reference are in the README:
👉 github.com/tarunprajapati88/GuardrailOps




Top comments (0)