Skip to main content

Lightweight explainable scoring and ranking engine for Python backends.

Project description

DecisionKit

CI PyPI version Python versions License: MIT

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.1) — 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 relative to the evaluated alternatives; min criteria inverted
TOPSIS topsis Vector normalization + closeness to ideal

Scores and “strong/weak” explanation labels are relative to the current alternative set, not absolute universal grades.

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-django integration package
  • Optional CLI for batch ranking

License

MIT — see LICENSE.

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

decisionkit-0.3.1.tar.gz (31.4 kB view details)

Uploaded Source

Built Distribution

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

decisionkit-0.3.1-py3-none-any.whl (26.5 kB view details)

Uploaded Python 3

File details

Details for the file decisionkit-0.3.1.tar.gz.

File metadata

  • Download URL: decisionkit-0.3.1.tar.gz
  • Upload date:
  • Size: 31.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for decisionkit-0.3.1.tar.gz
Algorithm Hash digest
SHA256 1080c1c30e53910a411b90b608b988fe2d886065dc8df1586ae39d04fa7d5764
MD5 c845dcb0e9e0b9592642e5d3d47a8923
BLAKE2b-256 617c2a55c1c83c1e0e229087713f0e7fadac66dea1c3d622c7bd5074ee87d21c

See more details on using hashes here.

File details

Details for the file decisionkit-0.3.1-py3-none-any.whl.

File metadata

  • Download URL: decisionkit-0.3.1-py3-none-any.whl
  • Upload date:
  • Size: 26.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.0

File hashes

Hashes for decisionkit-0.3.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3530718999d9f89e43ee85ea4d0572c2498b6103b17be64fe69538ea759f19ad
MD5 946e91124e79d3b45a37dac09e4d6818
BLAKE2b-256 62ffb2bd8970d1e3e6f7c3ded4df0bd55cd372521479d844e77d4b265b254b77

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