Metadata-Version: 2.4
Name: cendor-guardrails
Version: 1.2.0
Summary: A local-first gate for LLM apps: define a check — keyword, regex, URL, length, JSON-schema — attach it to a stage (input, tool call, tool output, output). Deterministic, microsecond, $0, and every decision is audit-grade evidence.
Project-URL: Homepage, https://github.com/cendorhq/cendor-libs
Project-URL: Documentation, https://cendor.ai/docs/guardrails
Project-URL: Repository, https://github.com/cendorhq/cendor-libs
Author: Raghav Mishra (PowerAI Labs)
License-Expression: Apache-2.0
License-File: LICENSE
License-File: NOTICE
Keywords: agents,ai,gate,governance,guardrails,llm,safety,validation
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: cendor-core<2.0,>=1.1
Provides-Extra: azure
Requires-Dist: azure-ai-contentsafety>=1.0; extra == 'azure'
Provides-Extra: bedrock
Requires-Dist: boto3>=1.34; extra == 'bedrock'
Provides-Extra: langid
Requires-Dist: py3langid>=0.3; extra == 'langid'
Provides-Extra: modelarmor
Requires-Dist: google-cloud-modelarmor>=0.2; extra == 'modelarmor'
Provides-Extra: promptguard
Requires-Dist: transformers>=4.40; extra == 'promptguard'
Provides-Extra: yaml
Requires-Dist: pyyaml>=6; extra == 'yaml'
Description-Content-Type: text/markdown

# cendor-guardrails

A local-first **gate** for LLM apps: define a check — keyword, regex, URL, length, JSON-schema —
attach it to a stage (`input`, `tool_call`, `tool_output`, `output`), and block, redact, or flag
before the model or a tool ever runs. No server, no account, no model call.

**Deterministic checks in microseconds for $0 — and every decision lands in a tamper-evident audit chain.**

![PyPI](https://img.shields.io/pypi/v/cendor-guardrails) ![license](https://img.shields.io/badge/license-Apache_2.0-blue) · `pip install cendor-guardrails`

```python
from cendor.core import instrument
from cendor.guardrails import install, rules

client = instrument(OpenAI())
install([                                           # one interceptor gates every call
    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 -> raises GuardrailTripped BEFORE the request is sent ($0 spent)
# a leaked key -> the provider receives "[redacted]" instead
```

## Highlights

- **Four intervention points** — gate the user turn (`input`), the model's request to call a tool
  (`tool_call`), the tool's result (`tool_output`), and the model's final answer (`output`).
  Matches Azure Foundry's intervention points and OpenAI's four decorator types.
- **Deterministic built-ins, no heavy deps** — `keyword_deny`, `regex_rule`, `url_allowlist` /
  `url_deny`, `length_bounds` (char + **exact** token bounds via `cendor.core.tokens`),
  `json_schema`, and `custom`. Regex/arithmetic only — offline, deterministic, $0.
- **Evidence, not just enforcement** — every trip or flag emits a `GuardrailDecision` on the
  `cendor.core` bus, so `cendor-acttrace` chains it as a tamper-evident `guardrail_decision` entry
  with **no import** between the two. "We blocked it" is in the hash chain, not a log line.
- **Three ways to use it** — pure `apply()` / `evaluate()`; framework-independent `install()` on
  the core seam (or **`scoped()`** for per-request gating on a concurrent server); and
  `Agent(guardrails=[…])` in `cendor-sdk` (all four in-loop stages + per-run override).
- **Bring-your-own model judge** — `rules.llm_judge` for open-ended risk, with per-guardrail
  `timeout` + `on_error` (fail-closed by default) and `cendor.guardrails.judge` helpers (verdict
  prompt + strict-JSON parsing). The judge rides an instrumented client, so its own spend is
  budgeted + audited.
- **Detection tiers you opt into** — a local classifier contract (`rules.classifier`,
  `rules.prompt_guard` behind the `[promptguard]` extra), `rules.language`, and hosted rails
  (`rules.bedrock_guardrail` / `azure_content_safety` / `model_armor` — duck-typed clients, metered
  by the vendor). Every hosted verdict still emits a **local** `guardrail_decision`: cloud check,
  local evidence. No jailbreak/PII-catch-rate claim ships without a reproduced, published benchmark.
- **Config as data + grounding** — `load_policy("guardrails.yaml")` builds deterministic rules from
  a versioned file and stamps its `policy_hash` / `policy_version` onto every decision (the audit
  chain proves which policy was live); `rules.groundedness` / `rules.denied_topics` gate on
  bring-your-own-embedding cosine similarity (RAG hallucination / off-topic), no bundled model.
- **Red-team it** — `run_redteam(guardrails, load_corpus("attacks.jsonl"))` reports the trip rate +
  false-positive rate against a labeled corpus **you** supply (cendor vends no attack data). A
  measurement, not a claim: publish a rate only with the corpus named.

```python
from cendor.guardrails import apply, guardrail, Verdict, GuardrailTripped

@guardrail(stage="output")
def must_be_json(payload, ctx):
    if not payload.strip().startswith("{"):
        return Verdict("block", reason="expected a JSON object")

try:
    apply([must_be_json], "output", model_text)      # raises GuardrailTripped on a block
except GuardrailTripped as e:
    print(e.decisions)                                 # the recorded decisions, block last
```

## How it plugs into your agent

`guardrails` is the **Gate** in the pipeline — `contextkit → squeeze → tokenguard → guardrails →
cassette → acttrace`. It imports **only** `cendor-core`: checks ride the same `instrument()` seam
and event bus every other library uses, so the same guardrail works under `cendor-sdk`, a bare
instrumented client, or beneath another framework — in Python and TypeScript alike.

**Honest limits:** the built-ins are deterministic, so they do not stop a *novel* adversarial
attack — a jailbreak they were never told about will pass. Pair them with a bring-your-own model
judge (`rules.llm_judge`, an adapter contract — you supply the call, and the extra latency/cost is
real) and treat the deterministic rules as the fast, free floor, not a ceiling. PII/secret
detection lives in `cendor-acttrace` (`guard(Policy…)`), not here.

*Part of the Cendor stack — github.com/cendorhq/cendor-libs. Powered by PowerAI Labs.*
