Skip to main content

A high-performance rules engine for Python - evaluate data against configurable rules and get clear verdicts

Project description

pylitmus

A high-performance rules engine for Python. Like a litmus test for your data - evaluate against configurable rules and get clear verdicts.

PyPI version Python License Tests Coverage

Features

  • YAML/JSON rule definitions - Business-friendly rule configuration
  • Hot-reload - Rules can be updated without restart
  • Multiple storage backends - Memory, database, file
  • Caching - Redis and in-memory caching support
  • Multiple scoring strategies - Sum, weighted, max
  • Flask integration - Easy integration with Flask apps
  • 18 built-in operators - Comparison, collection, string, null, temporal
  • Extensible - Custom evaluators and strategies

Installation

pip install pylitmus

# With database support
pip install pylitmus[database]

# With Redis caching
pip install pylitmus[redis]

# With Flask integration
pip install pylitmus[flask]

# All extras
pip install pylitmus[all]

Quick Start

from pylitmus import create_engine, Rule, Severity

# Create engine with inline rules
engine = create_engine(rules=[
    Rule(
        code='AMT_001',
        name='High Amount',
        description='Flag high amounts',
        category='AMOUNT',
        severity=Severity.HIGH,
        score=60,
        enabled=True,
        conditions={'field': 'amount', 'operator': 'greater_than', 'value': 5000}
    )
])

# Evaluate data
result = engine.evaluate({'amount': 6000})

print(f"Score: {result.total_score}")      # 60
print(f"Decision: {result.decision}")       # FLAG
print(f"Triggered: {[r.rule_code for r in result.triggered_rules]}")  # ['AMT_001']

YAML Rules

Define rules in YAML files for easy management:

# rules.yaml
rules:
  - code: "AMT_001"
    name: "High Amount"
    description: "Flag transactions over $5000"
    category: "AMOUNT"
    severity: "HIGH"
    score: 60
    enabled: true
    conditions:
      field: "amount"
      operator: "greater_than"
      value: 5000

  - code: "RISK_001"
    name: "High Risk Country"
    description: "Flag transactions from high-risk countries"
    category: "RISK"
    severity: "CRITICAL"
    score: 80
    enabled: true
    conditions:
      field: "country"
      operator: "in"
      value: ["XX", "YY", "ZZ"]

Load rules from file:

engine = create_engine(
    storage_backend='file',
    rules_file='rules.yaml'
)

Composite Conditions

Combine conditions with AND/OR logic:

conditions:
  all:  # AND
    - field: "amount"
      operator: "greater_than"
      value: 1000
    - any:  # OR
        - field: "is_new_customer"
          operator: "equals"
          value: true
        - field: "country"
          operator: "in"
          value: ["NG", "KE", "GH"]

Or use the alternative format:

conditions:
  type: "AND"
  conditions:
    - field: "amount"
      operator: "greater_than"
      value: 1000
    - field: "is_international"
      operator: "equals"
      value: true

Available Operators

Category Operators
Comparison equals, not_equals, greater_than, greater_than_or_equal, less_than, less_than_or_equal, between
Collection in, not_in, contains, not_contains
String starts_with, ends_with, matches_regex
Null is_null, is_not_null
Temporal within_days, before, after

Scoring Strategies

Sum Strategy (Default)

Adds up all triggered rule scores, capped at 100.

engine = create_engine(scoring_strategy='sum')

Weighted Strategy

Uses severity-based weights (LOW=1, MEDIUM=2, HIGH=3, CRITICAL=4).

engine = create_engine(scoring_strategy='weighted')

Max Strategy

Takes the highest score from triggered rules.

engine = create_engine(scoring_strategy='max')

Decision Thresholds

Customize decision boundaries:

engine = create_engine(
    decision_thresholds={
        'approve': 30,  # Score < 30 = APPROVE
        'review': 70    # Score 30-70 = REVIEW, >= 70 = FLAG
    }
)

Storage Backends

In-Memory

engine = create_engine(storage_backend='memory', rules=[...])

File-Based

engine = create_engine(
    storage_backend='file',
    rules_file='rules.yaml'  # or rules.json
)

Database

engine = create_engine(
    storage_backend='database',
    database_url='postgresql://localhost/mydb'
)

Caching

Memory Cache

engine = create_engine(
    cache_backend='memory',
    cache_ttl=300  # 5 minutes
)

Redis Cache

engine = create_engine(
    cache_backend='redis',
    cache_url='redis://localhost:6379/0',
    cache_ttl=600
)

No Cache

engine = create_engine(cache_backend='none')

Flask Integration

from flask import Flask
from pylitmus.integrations.flask import CmapRulesEngine, get_engine

app = Flask(__name__)
app.config['CMAP_RULES_FILE'] = 'rules.yaml'

rules_engine = CmapRulesEngine(app)

@app.route('/evaluate', methods=['POST'])
def evaluate():
    data = request.json
    engine = get_engine()
    result = engine.evaluate(data)
    return {
        'score': result.total_score,
        'decision': result.decision,
        'triggered_rules': [r.rule_code for r in result.triggered_rules]
    }

Nested Field Access

Access nested data using dot notation:

data = {
    'transaction': {
        'amount': 6000,
        'merchant': {
            'category': 'electronics'
        }
    }
}

# Rule condition
conditions:
  field: "transaction.merchant.category"
  operator: "equals"
  value: "electronics"

Pattern Matching

Advanced pattern matching capabilities:

from pylitmus import EnhancedPatternEngine

pattern_engine = EnhancedPatternEngine()

# Regex matching
pattern_engine.add_pattern('email', r'^[\w.-]+@[\w.-]+\.\w+$', 'regex')

# Fuzzy matching
pattern_engine.add_pattern('name', 'John Smith', 'fuzzy', threshold=0.8)

# Range matching
pattern_engine.add_pattern('age', {'min': 18, 'max': 65}, 'range')

# Check matches
result = pattern_engine.match_all({
    'email': 'user@example.com',
    'name': 'Jon Smith',
    'age': 25
})

API Reference

create_engine()

def create_engine(
    storage_backend: str = 'memory',
    database_url: str = None,
    rules_file: str = None,
    rules: List[Rule] = None,
    repository: RuleRepository = None,
    cache_backend: str = 'memory',
    cache_url: str = None,
    cache_ttl: int = 300,
    scoring_strategy: str = 'sum',
    decision_thresholds: Dict[str, int] = None,
) -> RuleEngine

RuleEngine.evaluate()

def evaluate(
    self,
    data: Dict[str, Any],
    context: Dict[str, Any] = None,
    filters: Dict[str, Any] = None
) -> AssessmentResult

AssessmentResult

@dataclass
class AssessmentResult:
    total_score: int           # Total calculated score
    decision: str              # APPROVE, REVIEW, or FLAG
    triggered_rules: List[RuleResult]  # Rules that matched
    processing_time_ms: float  # Processing time in ms

Full Example

from pylitmus import (
    create_engine,
    Rule,
    Severity,
    InMemoryRuleRepository,
    WeightedStrategy,
)

# Define rules
rules = [
    Rule(
        code='AMT_HIGH',
        name='High Amount',
        description='Flag high-value transactions',
        category='AMOUNT',
        severity=Severity.HIGH,
        score=60,
        enabled=True,
        conditions={'field': 'amount', 'operator': 'greater_than', 'value': 5000}
    ),
    Rule(
        code='NEW_CUSTOMER',
        name='New Customer',
        description='Flag new customer transactions',
        category='CUSTOMER',
        severity=Severity.MEDIUM,
        score=30,
        enabled=True,
        conditions={'field': 'is_new_customer', 'operator': 'equals', 'value': True}
    ),
    Rule(
        code='INTL_TXN',
        name='International Transaction',
        description='Flag international transactions',
        category='GEOGRAPHY',
        severity=Severity.LOW,
        score=20,
        enabled=True,
        conditions={'field': 'is_international', 'operator': 'equals', 'value': True}
    ),
]

# Create engine with weighted scoring
engine = create_engine(
    rules=rules,
    scoring_strategy='weighted',
    decision_thresholds={'approve': 25, 'review': 60}
)

# Evaluate transaction
result = engine.evaluate({
    'amount': 6000,
    'is_new_customer': True,
    'is_international': False
})

print(f"Total Score: {result.total_score}")
print(f"Decision: {result.decision}")
print(f"Triggered Rules: {[r.rule_code for r in result.triggered_rules]}")
print(f"Processing Time: {result.processing_time_ms:.2f}ms")

Documentation

License

MIT License - see LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pylitmus-1.0.0.tar.gz (50.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pylitmus-1.0.0-py3-none-any.whl (34.3 kB view details)

Uploaded Python 3

File details

Details for the file pylitmus-1.0.0.tar.gz.

File metadata

  • Download URL: pylitmus-1.0.0.tar.gz
  • Upload date:
  • Size: 50.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for pylitmus-1.0.0.tar.gz
Algorithm Hash digest
SHA256 645a286c1e405c3de95dfb58c60f0fed05204c3e91a575d445230cf2cad42f7c
MD5 0a43edc439ff2f9b7bfb386db49794c4
BLAKE2b-256 35a64f9e00283d98e6d3d4677630b936de2aa48c5a32c85eac9806f02c40e26e

See more details on using hashes here.

File details

Details for the file pylitmus-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: pylitmus-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 34.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for pylitmus-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b5c52f15205f404bfc012ee31574ca2020f9ba45a77eaca75c6ad8311dae485e
MD5 156d7335224cbafefc6f9f0c42266266
BLAKE2b-256 3208f454177df84d22ab17a54574793898370b89c5928fddab5ab992a1ca48c3

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page