Tutorial: First Integration
Embed BlazeRules in a minimal Python service loop that accumulates records into batches, evaluates them, and routes by decision.
Goal: wire BlazeRules into a small Python service that buffers incoming records, evaluates them one batch at a time, and routes each record by its decision.
Prerequisites
- The
blazerulesPython module onPYTHONPATH(Installation). - A
rules.yaml(Rule DSL) — the repo's sample works.
Step 1 — Construct and load once
Create the engine and load rules a single time at startup, not per request.
import blazerules
config = blazerules.EngineConfig()
config.output_detail = blazerules.OutputDetail.DECISIONS
engine = blazerules.RuleEngine(config)
engine.load_rules("rules.yaml")Step 2 — Accumulate records into a batch
BlazeRules is batch-first. Buffer incoming records (here as NDJSON lines) and flush on a size or time trigger.
import json, time
buffer: list[bytes] = []
FLUSH_SIZE = 4096
last_flush = time.monotonic()
def offer(record: dict) -> None:
buffer.append(json.dumps(record).encode())
def should_flush() -> bool:
return len(buffer) >= FLUSH_SIZE or (time.monotonic() - last_flush) > 0.05Step 3 — Evaluate the batch
Join the buffered lines into one NDJSON payload and evaluate once.
def flush():
global buffer, last_flush
if not buffer:
return None
payload = b"\n".join(buffer)
result = engine.evaluate_ndjson(payload)
buffer = []
last_flush = time.monotonic()
return resultStep 4 — Route by decision
Use the decision-group accessors instead of looping over every record in Python.
result = flush()
if result is not None:
groups = result.grouped_decision_indices() # {decision_label: [row indices]}
approved = result.indices_for_decision("APPROVE")
escalate = result.indices_for_not_decision("APPROVE")
print("records:", result.n_records, "matched:", result.n_matched)
for decision, rows in groups.items():
print(decision, "->", len(rows))Expected output
For a batch where most records are clean and a few trip a blocking rule, you'll see something like:
records: 4096 matched: 87
APPROVE -> 4009
REVIEW -> 71
BLOCK -> 16The exact numbers depend on your rules and data; the shape is what matters — one decision label per record, grouped for routing.
Validation
result.n_recordsequals the number of records you flushed.- The group counts sum to
n_records. result.messages_skippedis0for clean input. If it isn't, inspectresult.error_countsandresult.error_samples.
Failure mode: malformed or mistyped records
Ingest is tolerant by default — bad records are skipped and counted rather than crashing the batch.
config.ingest_error_mode = blazerules.IngestErrorMode.SKIP_AND_COUNT
config.type_mismatch_mode = blazerules.TypeMismatchMode.NULL_ON_TYPE_ERRORSwitch to IngestErrorMode.SKIP_TO_DEAD_LETTER or HARD_FAIL if you need stricter handling. See Error Reference.