Observed since 20 Oct, 21:28 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
agent-armour
Guard LLM tool calls with rules, scoring, and audit trails.
Guard LLM tool calls with rules, scoring, and audit trails.
cascade is a lightweight governance layer for AI agent tool calls. It sits
between your LLM and tool execution — evaluate every tool call against rules,
rank survivors by strategy, and audit every decision.
from cascade import DecisionPipeline
pipe = DecisionPipeline()
result = pipe.guard(
tool_calls=[
{"id": "1", "name": "search", "confidence": 0.92},
{"id": "2", "name": "delete", "confidence": 0.15},
],
rules=[
{"field": "confidence", "op": "gte", "value": 0.5},
{"field": "name", "op": "nin", "value": ["delete"]},
],
strategy="softmax",
top_k=1,
)
if result["selected"]:
safe = result["selected"][0]
print(f"Safe: {safe['name']} ({safe['confidence']})")
pip install cascade
Zero external dependencies. Optional extras extend the feature set:
pip install cascade[openai] # OpenAI SDK adapter
pip install cascade[anthropic] # Anthropic SDK adapter
pip install cascade[langchain] # LangChain adapter
pip install cascade[crewai] # CrewAI adapter
pip install cascade[gemini] # Gemini SDK adapter
pip install cascade[autogen] # AutoGen adapter
pip install cascade[yaml] # YAML policy files + cascade policy lint
Framework-specific adapters let you plug cascade governance into your existing agent code with minimal changes. Each adapter is a thin (<80 lines) layer — zero impact on the core codebase.
# OpenAI — auto-govern every chat.completions.create
from openai import OpenAI
from cascade import DecisionPipeline
from cascade.adapters.openai import wrap_openai_client
client = wrap_openai_client(
OpenAI(),
pipeline=DecisionPipeline(),
rules=[{"field": "name", "op": "nin", "value": ["delete_file", "exec"]}],
)
# LangChain — post-process agent output
from cascade.adapters.langchain import guard_agent_output
result = agent.invoke({"input": "search for papers on AI safety"})
result = guard_agent_output(result, pipeline=pipe, rules=[...])
guard() auto-writes JSONL audit trailsall_of / any_of / not_ for complex policiesblock / redirect / transform for automated remediationDefine governance rules in YAML for repeatable, audit-friendly policies.
# policy.yaml
name: strict-tools
description: Block dangerous tools
rules:
- field: name
op: nin
value: [delete_file, exec, rm]
- field: confidence
op: gte
value: 0.7
- all_of:
- field: name
op: eq
value: code_interpreter
- field: confidence
op: gte
value: 0.9
strategy: softmax
top_k: 1
cascade policy lint policy.yaml
cascade check --tool-calls @tools.json --policy policy.yaml
Composite rules (all_of / any_of / not_), @import directives, and
full schema validation are supported.
Every guard() decision is recorded in a SHA-256 hash-chained JSONL audit
trail. Each entry links to its predecessor via prev_hash, making the log
tamper-evident.
cascade audit verify
from cascade._audit import AuditTrail
trail = AuditTrail()
result = trail.verify() # {'valid': True, 'entries': 42, ...}
C1 (Gate) : Rule engine — 11 operators + AND/OR/NOT composition
C2 (Trigger) : Event triggers — condition callbacks + state machine
C3 (Selector) : Selection pressure — uniform/linear/softmax/threshold ranking
C4 (Feedback) : Feedback loop — binary/proportional/threshold reward
Linkage : C₃↔C₄ closed loop — rewards adjust future selection
| File | What it covers |
|---|---|
| docs/usage.md | guard() API, DecisionPipeline, AuditTrail |
| docs/rules.md | Leaf rules, rule presets, composite rules (all_of/any_of/not_) |
| docs/strategies.md | Selection strategies and when to use each |
| docs/cli.md | cascade check / policy lint / audit verify CLI reference |
| docs/owasp.md | OWASP Agentic Top 10 compliance mapping |
| CHANGELOG.md | Version history |
| Framework | Adapter | Lines | Install |
|---|---|---|---|
| OpenAI SDK | wrap_openai_client() / guard_openai_response() | ~70 | cascade[openai] |
| Anthropic SDK | guard_anthropic_response() / wrap_anthropic_client() | ~70 | cascade[anthropic] |
| LangChain | guard_agent_output() | ~55 | cascade[langchain] |
| CrewAI | guard_crew_output() / wrap_crew() | ~65 | cascade[crewai] |
| Gemini SDK | guard_gemini_response() / wrap_genai_client() | ~80 | cascade[gemini] |
| AutoGen | guard_agent_reply() / wrap_agent() | ~120 | cascade[autogen] |
| MCP Server | guarded_tool() / MCPServerGuard | ~100 | zero-dep (core) |
Each adapter is opt-in — the core stays zero-dependency. All adapters
live in src/cascade/adapters/ and import only what they need at runtime.
For custom framework integrations, use pipe.guard() directly:
response = client.chat.completions.create(..., tools=my_tools)
result = pipe.guard(
tool_calls=[{"id": t.id, "name": t.function.name, ...}],
rules=[{"field": "name", "op": "nin", "value": BLOCKED_TOOLS}],
)
Download counts and popularity will appear here as the collector receives them.
| Package / target | Requirement | Relationship |
|---|---|---|
openaiEnvironment{
"extras": "",
"marker": "extra == \"openai\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | openai>=1.0; extra == "openai" | runtime |
anthropicEnvironment{
"extras": "",
"marker": "extra == \"anthropic\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | anthropic>=0.30; extra == "anthropic" | runtime |
langchain-coreEnvironment{
"extras": "",
"marker": "extra == \"langchain\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | langchain-core>=0.3; extra == "langchain" | runtime |
crewaiEnvironment{
"extras": "",
"marker": "extra == \"crewai\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | crewai>=0.30; extra == "crewai" | runtime |
google-genaiEnvironment{
"extras": "",
"marker": "extra == \"gemini\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | google-genai>=1.0; extra == "gemini" | runtime |
pyautogenEnvironment{
"extras": "",
"marker": "extra == \"autogen\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pyautogen>=0.2; extra == "autogen" | runtime |
pyyamlEnvironment{
"extras": "",
"marker": "extra == \"yaml\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pyyaml>=6.0; extra == "yaml" | runtime |
pytestEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pytest>=7; extra == "dev" | runtime |
ruffEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | ruff>=0.6; extra == "dev" | runtime |
pyrightEnvironment{
"extras": "",
"marker": "extra == \"dev\"",
"requires_python": ">=3.10",
"evaluation": "NOT_EVALUATED"
} | pyright>=1.1; extra == "dev" | runtime |
bdist_wheel · 47.91 KB · Python py3
sdist · 51.97 KB · Python source
Observed since 20 Oct, 21:28 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package_version:32c4e6a1bf5a2e6a531b4c81aa7887d9467df9fa09441f843cb6950684ea36c7