Constraint Engine
JSON predicates (auto-tier) and SHACL shapes (formal-tier)
The Constraint Engine evaluates business rules against proposed actions using two tiers: JSON predicates for rapid, code-free constraint authoring, and SHACL shapes for formal, logic-based validation.
How It Works
Auto-Tier: JSON Predicates
JSON predicates are the primary constraint format. They are fast to author, easy to understand, and sufficient for most business rules.
The PraxomPolicyEngine in policy_engine.py evaluates predicates using evaluate_predicate():
predicate = {
"all": [
{"field": "arguments.amount", "operator": "<=", "value": 10000},
{"field": "arguments.currency", "operator": "in", "value": ["USD", "EUR"]},
{"field": "arguments.status", "operator": "!=", "value": "deleted"}
]
}
result = evaluate_predicate(payload, predicate)Supported operators:
| Operator | Description | Example |
|---|---|---|
== (alias =) | Equal | {"field": "status", "operator": "==", "value": "active"} |
!= | Not equal | {"field": "status", "operator": "!=", "value": "deleted"} |
> | Greater than | {"field": "record_count", "operator": ">", "value": 50} |
>= | Greater than or equal | {"field": "amount", "operator": ">=", "value": 100} |
< | Less than | {"field": "amount", "operator": "<", "value": 50000} |
<= | Less than or equal | {"field": "amount", "operator": "<=", "value": 10000} |
in | Field value in list | {"field": "currency", "operator": "in", "value": ["USD", "EUR"]} |
not_in | Field value not in list | {"field": "region", "operator": "not_in", "value": ["restricted"]} |
contains | Field (a collection) contains value | {"field": "gvc.affected_entity_types", "operator": "contains", "value": "Patient"} |
regex | Regular expression match | {"field": "email", "operator": "regex", "value": "^.*@company\\.com$"} |
exists | Field presence check | {"field": "arguments.approval_id", "operator": "exists", "value": true} |
Glob patterns (salesforce.*, *.delete_*) apply to tool-name matching on the constraint, not as a field operator.
The JsonPredicateEvaluator class (json_predicate_evaluator.py) wraps this logic as a ConstraintEvaluator implementation for the pluggable evaluator registry.
Formal-Tier: SHACL Shapes
SHACL (Shapes Constraint Language) provides formal-logic validation for complex business rules that benefit from ontological reasoning.
SHACL Generator (shacl_generator.py) converts structured JSON policy definitions into SHACL Turtle:
# Input: JSON constraint definition
# Output: SHACL Turtle that pyshacl can evaluateSHACL Evaluator (shacl_evaluator.py) runs pyshacl against action payloads converted to RDF:
result = evaluate_shacl(payload, shacl_graph)
# result.conforms: bool
# result.violations: list of SHACL violationsSHACL Constraint Evaluator (shacl_constraint_evaluator.py) wraps the SHACL evaluator as a ConstraintEvaluator for the pluggable registry, supporting tool pattern matching via fnmatch.
Architecture
Both tiers feed into the same policy engine:
Proposed Action
↓
PraxomPolicyEngine
├── JSON Predicate Evaluator (auto-tier, fast)
└── SHACL Constraint Evaluator (formal-tier, rigorous)
↓
PolicyDecision (merged results from all matching evaluators)Auto-tier constraints are evaluated first for performance. SHACL constraints are evaluated only if needed (when formal-tier constraints exist for the matching tool pattern).
Configuration
Constraints are created via the REST API:
# Auto-tier (JSON predicate)
POST /api/v1/praxom/constraints
{
"tier": "auto",
"tool_patterns": ["salesforce.update_*"],
"predicate": {"all": [{"field": "arguments.amount", "operator": "<=", "value": 10000}]},
"verdict_on_match": "deny"
}
# Formal-tier (SHACL)
POST /api/v1/praxom/constraints
{
"tier": "formal",
"tool_patterns": ["ehr.prescribe_*"],
"shacl_turtle": "<SHACL shape definition>",
"verdict_on_match": "deny"
}Technical Details
- JSON predicates support dot-notation field access for nested payloads:
arguments.contact.address.zip - Regex evaluation is sandboxed with a 1-second timeout to prevent ReDoS
- SHACL shapes use safe identifier allowlists to prevent injection via property paths
- Constraint evaluation is deterministic — no LLM, no randomness, same input always produces same output
- The
_check_requirements()function validates structural requirements (required fields must be present)