Lightweight explainable scoring and ranking engine for Python backends.
Project description
DecisionKit
DecisionKit is a lightweight explainable scoring and ranking engine for Python backends.
Describe criteria, weights, constraints, penalties, and bonuses — in code or config — then get ranked alternatives with scores, human-readable explanations, and audit-ready output.
No machine learning. No database. No web UI. Core has zero runtime dependencies.
Project status
Beta (v0.3.0) — useful for real backend workflows; API aims to stay stable across minor releases. See CHANGELOG.md.
Installation
pip install decisionkit
Optional YAML support:
pip install "decisionkit[yaml]"
From source:
pip install -e ".[dev]"
Quick start
from decisionkit import DecisionModel, Criterion
model = DecisionModel(
criteria=[
Criterion("relevance", weight=0.5, direction="max"),
Criterion("rating", weight=0.3, direction="max"),
Criterion("workload", weight=0.2, direction="min"),
],
method="weighted_sum",
explain=True,
)
result = model.rank([
{"id": "reviewer_1", "relevance": 0.92, "rating": 4.8, "workload": 7},
{"id": "reviewer_2", "relevance": 0.81, "rating": 4.5, "workload": 2},
])
print(result.best.id)
print(result.explain())
Config-driven usage
from decisionkit import DecisionModel
config = {
"method": "weighted_sum",
"explain": True,
"criteria": [
{"name": "relevance", "weight": 0.5, "direction": "max"},
{"name": "rating", "weight": 0.3, "direction": "max"},
{"name": "workload", "weight": 0.2, "direction": "min"},
],
"constraints": [
{
"name": "available_only",
"field": "available",
"operator": "eq",
"value": True,
"reason": "Reviewer must be available",
}
],
"penalties": [
{
"name": "high_workload_penalty",
"field": "workload",
"operator": "gt",
"value": 5,
"amount": 0.1,
}
],
"bonuses": [
{
"name": "exact_topic_bonus",
"field": "topic_match",
"operator": "gte",
"value": 0.9,
"amount": 0.05,
}
],
}
model = DecisionModel.from_dict(config)
result = model.rank(
[
{
"id": "reviewer_1",
"relevance": 0.92,
"rating": 4.8,
"workload": 7,
"available": True,
"topic_match": 0.95,
},
{
"id": "reviewer_2",
"relevance": 0.81,
"rating": 4.5,
"workload": 2,
"available": True,
"topic_match": 0.8,
},
],
context={"max_workload": 5},
)
print(result.best.id)
print(result.to_audit_dict(include_hash=True)["audit_hash"])
JSON and optional YAML helpers:
model = DecisionModel.from_json(model.to_json())
# pip install "decisionkit[yaml]"
# model = DecisionModel.from_yaml(model.to_yaml())
Rules: constraints, penalties, bonuses
Operators: eq, ne, gt, gte, lt, lte, in, not_in, contains, between.
Context-aware rule:
{
"name": "within_capacity",
"field": "workload",
"operator": "lte",
"context": "max_workload",
}
Compound rule (all = AND, any = OR):
{
"name": "eligible",
"all": [
{"field": "available", "operator": "eq", "value": True},
{"field": "workload", "operator": "lte", "context": "max_workload"},
],
}
Audit export and hash
audit = result.to_audit_dict(
decision_id="req-123",
metadata={"tenant": "acme"},
timestamp="2026-07-10T10:00:00Z",
include_hash=True,
)
digest = result.audit_hash() # sha256 of canonical decision payload
The digest covers method, model, context, inputs, exclusions, ranking, and best alternative. It excludes decision_id, metadata, and timestamp by default. Details: docs/audit.md.
FastAPI integration
from fastapi import FastAPI, HTTPException
from decisionkit import DecisionModel, ValidationError
app = FastAPI()
@app.post("/rank")
def rank(payload: dict):
try:
model = DecisionModel.from_dict(payload["model"])
result = model.rank(
payload["alternatives"],
context=payload.get("context", {}),
)
return result.to_audit_dict(
decision_id=payload.get("decision_id"),
include_hash=True,
)
except ValidationError as exc:
raise HTTPException(status_code=400, detail=str(exc)) from exc
See examples/fastapi_example.py.
Django service-layer integration
Keep DecisionKit in a service module; store config in settings/admin/DB:
from decisionkit import DecisionModel
def rank_reviewers(candidates: list[dict], model_config: dict) -> dict:
model = DecisionModel.from_dict(model_config)
result = model.rank(candidates, context={"max_workload": 5})
return result.to_audit_dict(decision_id="editorial-assign", include_hash=True)
See examples/django_service_example.py.
How DecisionKit compares
| Approach | Fit |
|---|---|
Ad-hoc if / spreadsheet scoring |
Fast to start, hard to explain, test, or reuse |
| Generic rule engines | Great for branching logic; weaker at weighted ranking + score breakdowns |
| Academic MCDA toolkits | Broad method coverage; often awkward for backend APIs and audits |
| DecisionKit | Backend-friendly weighted ranking with rules, explanations, and audit hashes |
DecisionKit is not trying to replace every MCDA method. It focuses on explainable scoring you can ship in Django/FastAPI services.
Supported methods
| Method | Key | Notes |
|---|---|---|
| Weighted sum | weighted_sum |
Min-max normalization; min criteria inverted |
| TOPSIS | topsis |
Vector normalization + closeness to ideal |
Examples
python examples/reviewer_selection.py
python examples/vendor_ranking.py
python examples/task_prioritization.py
python examples/risk_scoring.py
python examples/django_service_example.py
More: docs/examples.md
Documentation
Development
pip install -e ".[dev]"
python -m ruff check src tests examples
python -m mypy src
python -m pytest --cov=decisionkit --cov-report=term-missing
CI runs on Python 3.11 and 3.12 (see .github/workflows/ci.yml).
Release steps: RELEASE.md
Roadmap
v0.4.0
- AHP method (optional, still backend-friendly)
- Nested boolean rule trees (beyond flat
all/any) - Signed / hashed decision record helpers for long-term storage
Later
- ELECTRE / PROMETHEE (only if API stays simple)
decisionkit-djangointegration package- Optional CLI for batch ranking
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file decisionkit-0.3.0.post1.tar.gz.
File metadata
- Download URL: decisionkit-0.3.0.post1.tar.gz
- Upload date:
- Size: 30.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d7e8a6b8a985705d4fdd50ca9299d908876725a1a05e6dd302a0623a0bbc941
|
|
| MD5 |
dc756152d27d9c776037f9d1108f7020
|
|
| BLAKE2b-256 |
754d79e7c7cefa5eae22f7952ebd266513d53a092c2c2680be22d0e29c666405
|
File details
Details for the file decisionkit-0.3.0.post1-py3-none-any.whl.
File metadata
- Download URL: decisionkit-0.3.0.post1-py3-none-any.whl
- Upload date:
- Size: 26.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0111e8d9afb8e7fa920ae807126aa36f43a79a02bc513334ebd56387425d02db
|
|
| MD5 |
d94a1552f6c96bf270f0a689fefb495a
|
|
| BLAKE2b-256 |
4402a1feda110842b0dec1ee515bf0e6d5a35f768df13b7187003bc5f03e4074
|