Libraries/guardrails
guardrailsGate

Blocked before the call,
for $0.00.

Define a deterministic check and attach it to a stage — a denied keyword, a regex, a URL allowlist, a length bound, a JSON-schema. Block, redact, or flag before the model or a tool ever runs. Microseconds, offline, no model call — and every decision lands in the audit chain.

$ pip install cendor-guardrailsSee how it gates ↓
support_agent.pyone interceptor, gated
from cendor.core import instrument
from cendor.guardrails import install, rules

client = instrument(OpenAI())
install([
    rules.keyword_deny(["ignore previous instructions"], action="block"),
    rules.regex_rule(r"\bsk-[A-Za-z0-9]{20,}\b", action="redact", stage="input"),
    rules.url_allowlist(["docs.cendor.ai"], stage="input"),
])

client.chat.completions.create(model="gpt-4o", messages=msgs)
# blocked prompt → GuardrailTripped BEFORE the request is sent ($0.00 spent)
# leaked key    → the provider receives "[redacted]" instead of the secret
The four stages

Gate the loop where it matters — not just the answer.

A guardrail is attached to one or more intervention points, matching Azure Foundry's intervention points and OpenAI's four decorator types. Output-only checks can't stop a tool's side effects — so gate the tool_call to stop a dangerous action before it runs.

Stage
Gates
On block
input
the user turn, before the model call
raises — pre-spend, $0
tool_call
the model's request to call a tool
returns [blocked …] — the loop continues, the tool never runs
tool_output
a tool's result, before the model sees it
replaces the result with [blocked …]
output
the model's final answer
raises (after generation)

A check returns a Verdict to trip, or nothing to pass. block fails closed · redact replaces the payload and continues · flag records and continues. Evaluation is blocking (v0): the built-ins are µs-scale, so parallel-with-the-model buys nothing.

Deterministic built-ins

Regex and arithmetic. No model, no network, no ML.

This is the local-first claim: every built-in is deterministic — microseconds, offline, $0. AWS itself prices this tier (regex + word filters) at zero; a local library owns it.

keyword_deny

trip when any denied word appears (substring, case-insensitive by default)

regex_rule

trip when a pattern matches; action="redact" substitutes each match

url_allowlist / url_deny

a URL's host is not allowlisted / is denied (subdomains match)

length_bounds

the payload exceeds a char and/or exact token bound (counts via core.tokens)

json_schema

the output isn't valid JSON, or violates a minimal type/required/properties/items schema

custom

your own check(payload, ctx) — sync or async

Deliberately not built in: PII/secret detection lives in acttrace's validator-gated catalogue (guard(Policy…)) — one detection engine, not two. llm_judge(judge) is a bring-your-own-model adapter contract, not a bundled classifier: you supply the call, and its latency/cost is stated honestly.

Three ways to use it

Pure, at the seam, or in the loop.

Pureapply(guardrails, stage, payload) / evaluate(...) gate a payload directly (sync + async).
Framework-independentinstall(guardrails) registers one core interceptor, so every instrumented client call is gated under any framework or a bare SDK.
In an agent loopAgent(guardrails=[…]) in cendor-sdk wires all four stages, with a per-run override.
Evidence, not just enforcement

"We blocked it" — in the hash chain, not a log line.

Every trip or flag emits a GuardrailDecision on core's bus. If an AuditLog is attached, it chains that decision as a tamper-evident guardrail_decision entry — the guardrail's name, stage, action, and reason (never the raw payload). No import between the two libraries: acttrace duck-types the decision.

No competitor pairs a provider-agnostic, local-first gate with audit-grade evidence in Python and TypeScript. That's the lane.

Honest limits

Where the edges are — by design.

Deterministic checks don't stop novel adversarial attacks.

The built-ins match exactly what you configure. A jailbreak they were never told about will pass. Pair them with a llm_judge adapter for open-ended risk — the deterministic rules are the free floor, not a ceiling.

An LLM judge costs real tokens and latency.

Where the built-ins are microseconds and $0, an extra model call is typically seconds and billed. llm_judge is a contract precisely so that cost is yours to measure — not assumed.

The standalone output stage is post-flight.

Via install(), output guardrails inspect the completed call and raise after it ran. Streamed deltas already shown can't be unshown.

PII/secret detection isn't here.

Use acttrace's guard(Policy…) for validator-gated detectors — one detection engine, kept in one place.

Get started

Gated in one line.

$ pip install cendor-guardrails

guardrails docs → · guardrails cookbook → · works alone, composes with the Cendor stack