decision-toolkit 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- decision_toolkit-0.1.0/.github/workflows/ci.yml +56 -0
- decision_toolkit-0.1.0/.github/workflows/release.yml +85 -0
- decision_toolkit-0.1.0/.gitignore +30 -0
- decision_toolkit-0.1.0/CHANGELOG.md +68 -0
- decision_toolkit-0.1.0/CONTRIBUTING.md +83 -0
- decision_toolkit-0.1.0/LICENSE +21 -0
- decision_toolkit-0.1.0/PKG-INFO +306 -0
- decision_toolkit-0.1.0/README.md +262 -0
- decision_toolkit-0.1.0/SECURITY.md +46 -0
- decision_toolkit-0.1.0/THIRD_PARTY_NOTICES.md +72 -0
- decision_toolkit-0.1.0/bench/README.md +88 -0
- decision_toolkit-0.1.0/bench/__init__.py +1 -0
- decision_toolkit-0.1.0/bench/load.py +163 -0
- decision_toolkit-0.1.0/bench/results/2026-10-20-calibration.json +311 -0
- decision_toolkit-0.1.0/bench/results/2026-10-20-load.json +31 -0
- decision_toolkit-0.1.0/bench/results/2026-10-20-smoke.json +246 -0
- decision_toolkit-0.1.0/bench/run.py +285 -0
- decision_toolkit-0.1.0/bench/suites/calibration.jsonl +10 -0
- decision_toolkit-0.1.0/bench/suites/smoke.jsonl +5 -0
- decision_toolkit-0.1.0/docs/BENCH.md +66 -0
- decision_toolkit-0.1.0/docs/CLI.md +239 -0
- decision_toolkit-0.1.0/docs/CORE.md +289 -0
- decision_toolkit-0.1.0/docs/EXAMPLES.md +215 -0
- decision_toolkit-0.1.0/docs/OPERATIONS.md +178 -0
- decision_toolkit-0.1.0/docs/RELEASE.md +116 -0
- decision_toolkit-0.1.0/docs/REST.md +307 -0
- decision_toolkit-0.1.0/docs/ROADMAP.md +35 -0
- decision_toolkit-0.1.0/docs/models/README.md +27 -0
- decision_toolkit-0.1.0/docs/models/gliner2.5-decide.md +58 -0
- decision_toolkit-0.1.0/docs/models/gliner2.5-multi.md +57 -0
- decision_toolkit-0.1.0/docs/models/julia-1.md +58 -0
- decision_toolkit-0.1.0/docs/models/laya.md +65 -0
- decision_toolkit-0.1.0/docs/models/opendecider-nano.md +58 -0
- decision_toolkit-0.1.0/pyproject.toml +101 -0
- decision_toolkit-0.1.0/src/decision_toolkit/__init__.py +102 -0
- decision_toolkit-0.1.0/src/decision_toolkit/__main__.py +8 -0
- decision_toolkit-0.1.0/src/decision_toolkit/adapters/__init__.py +165 -0
- decision_toolkit-0.1.0/src/decision_toolkit/adapters/base.py +134 -0
- decision_toolkit-0.1.0/src/decision_toolkit/adapters/gliner2.py +144 -0
- decision_toolkit-0.1.0/src/decision_toolkit/adapters/gliner2_decide.py +117 -0
- decision_toolkit-0.1.0/src/decision_toolkit/adapters/julia.py +168 -0
- decision_toolkit-0.1.0/src/decision_toolkit/adapters/laya.py +225 -0
- decision_toolkit-0.1.0/src/decision_toolkit/adapters/opendecider.py +144 -0
- decision_toolkit-0.1.0/src/decision_toolkit/calibration.py +79 -0
- decision_toolkit-0.1.0/src/decision_toolkit/catalog.py +472 -0
- decision_toolkit-0.1.0/src/decision_toolkit/cli.py +1277 -0
- decision_toolkit-0.1.0/src/decision_toolkit/config.py +90 -0
- decision_toolkit-0.1.0/src/decision_toolkit/contracts/__init__.py +58 -0
- decision_toolkit-0.1.0/src/decision_toolkit/contracts/common.py +61 -0
- decision_toolkit-0.1.0/src/decision_toolkit/contracts/mapping.py +165 -0
- decision_toolkit-0.1.0/src/decision_toolkit/contracts/openai_decisions_beta.py +133 -0
- decision_toolkit-0.1.0/src/decision_toolkit/contracts/systemone_v1.py +140 -0
- decision_toolkit-0.1.0/src/decision_toolkit/decision_model.py +692 -0
- decision_toolkit-0.1.0/src/decision_toolkit/engine/__init__.py +111 -0
- decision_toolkit-0.1.0/src/decision_toolkit/errors.py +138 -0
- decision_toolkit-0.1.0/src/decision_toolkit/limits.py +114 -0
- decision_toolkit-0.1.0/src/decision_toolkit/metrics.py +198 -0
- decision_toolkit-0.1.0/src/decision_toolkit/models.py +588 -0
- decision_toolkit-0.1.0/src/decision_toolkit/observability.py +145 -0
- decision_toolkit-0.1.0/src/decision_toolkit/py.typed +0 -0
- decision_toolkit-0.1.0/src/decision_toolkit/registry.py +283 -0
- decision_toolkit-0.1.0/src/decision_toolkit/results.py +355 -0
- decision_toolkit-0.1.0/src/decision_toolkit/schema.py +516 -0
- decision_toolkit-0.1.0/src/decision_toolkit/server.py +930 -0
- decision_toolkit-0.1.0/src/decision_toolkit/tracing.py +44 -0
- decision_toolkit-0.1.0/tests/__init__.py +1 -0
- decision_toolkit-0.1.0/tests/conftest.py +117 -0
- decision_toolkit-0.1.0/tests/test_adapters_gliner2.py +181 -0
- decision_toolkit-0.1.0/tests/test_adapters_gliner2_decide.py +86 -0
- decision_toolkit-0.1.0/tests/test_adapters_julia.py +110 -0
- decision_toolkit-0.1.0/tests/test_adapters_laya.py +186 -0
- decision_toolkit-0.1.0/tests/test_adapters_opendecider.py +104 -0
- decision_toolkit-0.1.0/tests/test_bench.py +29 -0
- decision_toolkit-0.1.0/tests/test_catalog.py +98 -0
- decision_toolkit-0.1.0/tests/test_cli.py +663 -0
- decision_toolkit-0.1.0/tests/test_config.py +53 -0
- decision_toolkit-0.1.0/tests/test_core_phase1.py +324 -0
- decision_toolkit-0.1.0/tests/test_decision_model.py +115 -0
- decision_toolkit-0.1.0/tests/test_fuzz_rest.py +112 -0
- decision_toolkit-0.1.0/tests/test_integration_gliner2.py +72 -0
- decision_toolkit-0.1.0/tests/test_models.py +234 -0
- decision_toolkit-0.1.0/tests/test_operations.py +177 -0
- decision_toolkit-0.1.0/tests/test_results.py +52 -0
- decision_toolkit-0.1.0/tests/test_schema.py +186 -0
- decision_toolkit-0.1.0/tests/test_server.py +508 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
name: python ${{ matrix.python }}
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
strategy:
|
|
18
|
+
fail-fast: false
|
|
19
|
+
matrix:
|
|
20
|
+
python: ["3.12", "3.13"]
|
|
21
|
+
env:
|
|
22
|
+
PYTHONPATH: src
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
- uses: actions/setup-python@v5
|
|
26
|
+
with:
|
|
27
|
+
python-version: ${{ matrix.python }}
|
|
28
|
+
cache: pip
|
|
29
|
+
- name: Install test dependencies from source
|
|
30
|
+
run: |
|
|
31
|
+
python -m pip install --upgrade pip
|
|
32
|
+
python -m pip install \
|
|
33
|
+
pytest ruff PyYAML huggingface_hub fastapi httpx pydantic numpy openai
|
|
34
|
+
- name: Lint
|
|
35
|
+
run: ruff check src tests bench
|
|
36
|
+
- name: Unit and contract tests (source tree, no wheel)
|
|
37
|
+
run: pytest -q
|
|
38
|
+
- name: CLI smoke from source
|
|
39
|
+
run: |
|
|
40
|
+
python -m decision_toolkit --version
|
|
41
|
+
python -m decision_toolkit --home "$RUNNER_TEMP/dtk-home" doctor --json
|
|
42
|
+
|
|
43
|
+
audit:
|
|
44
|
+
name: dependency audit
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
steps:
|
|
47
|
+
- uses: actions/checkout@v4
|
|
48
|
+
- uses: actions/setup-python@v5
|
|
49
|
+
with:
|
|
50
|
+
python-version: "3.13"
|
|
51
|
+
- name: Install audit tool and runtime dependencies
|
|
52
|
+
run: |
|
|
53
|
+
python -m pip install --upgrade pip pip-audit
|
|
54
|
+
python -m pip install PyYAML huggingface_hub fastapi httpx pydantic numpy
|
|
55
|
+
- name: pip-audit
|
|
56
|
+
run: pip-audit
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
inputs:
|
|
9
|
+
target:
|
|
10
|
+
description: "Publish target (tag pushes always publish TestPyPI then PyPI)"
|
|
11
|
+
type: choice
|
|
12
|
+
options: [testpypi]
|
|
13
|
+
default: testpypi
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
build:
|
|
20
|
+
name: build distributions
|
|
21
|
+
runs-on: ubuntu-latest
|
|
22
|
+
steps:
|
|
23
|
+
- uses: actions/checkout@v4
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: "3.12"
|
|
27
|
+
- name: Build sdist and wheel
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade build twine
|
|
30
|
+
python -m build
|
|
31
|
+
python -m twine check dist/*
|
|
32
|
+
- uses: actions/upload-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: distributions
|
|
35
|
+
path: dist/
|
|
36
|
+
|
|
37
|
+
publish-testpypi:
|
|
38
|
+
name: publish TestPyPI
|
|
39
|
+
if: startsWith(github.ref, 'refs/tags/v') || inputs.target == 'testpypi'
|
|
40
|
+
needs: build
|
|
41
|
+
runs-on: ubuntu-latest
|
|
42
|
+
environment: testpypi
|
|
43
|
+
permissions:
|
|
44
|
+
id-token: write
|
|
45
|
+
steps:
|
|
46
|
+
- uses: actions/download-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: distributions
|
|
49
|
+
path: dist/
|
|
50
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
51
|
+
with:
|
|
52
|
+
repository-url: https://test.pypi.org/legacy/
|
|
53
|
+
|
|
54
|
+
publish-pypi:
|
|
55
|
+
name: publish PyPI
|
|
56
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
57
|
+
needs: publish-testpypi
|
|
58
|
+
runs-on: ubuntu-latest
|
|
59
|
+
environment: pypi
|
|
60
|
+
permissions:
|
|
61
|
+
id-token: write
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/download-artifact@v4
|
|
64
|
+
with:
|
|
65
|
+
name: distributions
|
|
66
|
+
path: dist/
|
|
67
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
68
|
+
|
|
69
|
+
github-release:
|
|
70
|
+
name: GitHub release
|
|
71
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
72
|
+
needs: publish-pypi
|
|
73
|
+
runs-on: ubuntu-latest
|
|
74
|
+
permissions:
|
|
75
|
+
contents: write
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
- uses: actions/download-artifact@v4
|
|
79
|
+
with:
|
|
80
|
+
name: distributions
|
|
81
|
+
path: dist/
|
|
82
|
+
- name: Create release
|
|
83
|
+
env:
|
|
84
|
+
GH_TOKEN: ${{ github.token }}
|
|
85
|
+
run: gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --generate-notes --verify-tag
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
build/
|
|
8
|
+
dist/
|
|
9
|
+
*.egg-info/
|
|
10
|
+
.eggs/
|
|
11
|
+
|
|
12
|
+
# Virtual environments
|
|
13
|
+
.venv/
|
|
14
|
+
venv/
|
|
15
|
+
|
|
16
|
+
# Test caches
|
|
17
|
+
.pytest_cache/
|
|
18
|
+
.coverage
|
|
19
|
+
htmlcov/
|
|
20
|
+
.ruff_cache/
|
|
21
|
+
|
|
22
|
+
# Local data
|
|
23
|
+
*.local
|
|
24
|
+
home/
|
|
25
|
+
/models/
|
|
26
|
+
|
|
27
|
+
# OS / editors
|
|
28
|
+
.DS_Store
|
|
29
|
+
.idea/
|
|
30
|
+
.vscode/
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented here. The project has not
|
|
4
|
+
published a distribution artifact yet; versions are repository milestones.
|
|
5
|
+
|
|
6
|
+
## [Unreleased]
|
|
7
|
+
|
|
8
|
+
### Phase 6 — packaging
|
|
9
|
+
|
|
10
|
+
- Added `py.typed`, project URLs, co-author metadata and sdist inclusion.
|
|
11
|
+
- Wheel and sdist build successfully; `twine check` passes with current tooling.
|
|
12
|
+
- Wheel installs in a clean venv with `[onnx,server,tracing]`; `dtk` and the
|
|
13
|
+
REST server work from the installed artifact.
|
|
14
|
+
- Added a Trusted Publishing release workflow for TestPyPI → PyPI and GitHub
|
|
15
|
+
Releases.
|
|
16
|
+
- No artifact has been uploaded yet; PyPI/TestPyPI publishing requires the
|
|
17
|
+
configured environments and approval.
|
|
18
|
+
- Added `docs/RELEASE.md`.
|
|
19
|
+
|
|
20
|
+
### Phase 5 — operations
|
|
21
|
+
|
|
22
|
+
- Per-key rate limiting with `429` + `Retry-After` (disabled by default).
|
|
23
|
+
- Bounded inference concurrency (`--max-concurrent`) with `503 server_busy`
|
|
24
|
+
instead of unbounded queueing.
|
|
25
|
+
- Prometheus `/metrics`: HTTP, decision, token, model-load, rate-limit, busy,
|
|
26
|
+
inflight and model-cache metrics with low-cardinality labels.
|
|
27
|
+
- Optional OpenTelemetry spans (`--tracing`) with a no-op fallback.
|
|
28
|
+
- `bench/load.py` concurrent load probe and a versioned backpressure result.
|
|
29
|
+
- Deterministic REST fuzz/negative corpus and PII sentinel tests.
|
|
30
|
+
- `pip-audit` CI job; `docs/OPERATIONS.md` runbook.
|
|
31
|
+
- Still no wheel/PyPI/tag.
|
|
32
|
+
|
|
33
|
+
### Phase 4 — reproducibility
|
|
34
|
+
|
|
35
|
+
- Pinned every catalog variant to a HuggingFace commit SHA.
|
|
36
|
+
- Recorded SHA256 for every declared model file and external-data file.
|
|
37
|
+
- `dtk models verify` now treats the catalog hashes as the source of truth and
|
|
38
|
+
also verifies `.onnx.data` files.
|
|
39
|
+
- Model downloads reject files that do not match the pinned hashes.
|
|
40
|
+
- Added a reproducible benchmark/calibration harness in `bench/` with versioned
|
|
41
|
+
results and a synthetic calibration suite.
|
|
42
|
+
- Added source-only CI for Python 3.12/3.13 (lint, tests, CLI smoke; no wheel).
|
|
43
|
+
- Added model-card reproducibility sections, `docs/BENCH.md`, contributing,
|
|
44
|
+
security and third-party notices.
|
|
45
|
+
|
|
46
|
+
### Phase 3 — CLI
|
|
47
|
+
|
|
48
|
+
- Consistent `--json`, quiet/verbose modes and exit codes (`0/1/2/3/130`).
|
|
49
|
+
- JSON/YAML questions files, JSONL batching, custom `--adapter/--files`.
|
|
50
|
+
- Variant-aware `use/current/delete` and strict `verify --all`.
|
|
51
|
+
- `config get/set/list`, `version`, `completion`.
|
|
52
|
+
- `serve --workers/--reload`, API key, CORS, log format and warmup.
|
|
53
|
+
|
|
54
|
+
### Phase 2 — REST
|
|
55
|
+
|
|
56
|
+
- Versioned System One 1.14 and OpenAI Decisions beta projections.
|
|
57
|
+
- Bearer auth, per-request model/variant, usage, standard error envelope.
|
|
58
|
+
- OpenAI-shaped `/v1/models`, rich `/v1/decision-models`, readiness `503`.
|
|
59
|
+
- Request ids, access logs, body limits, CORS and timeouts.
|
|
60
|
+
|
|
61
|
+
### Phase 1 — core
|
|
62
|
+
|
|
63
|
+
- Typed `DecisionModel` API with lifecycle, batching, async, usage, warnings,
|
|
64
|
+
normalized confidence, capabilities, structured events and atomic model store.
|
|
65
|
+
|
|
66
|
+
## [0.1.0] — unreleased
|
|
67
|
+
|
|
68
|
+
The version is not tagged or packaged yet. Wheel/PyPI construction is deferred.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
decision-toolkit is a curated runtime, not a mirror of model hubs. Changes that
|
|
4
|
+
add a model or an adapter must bring **real evidence**, not a promising README.
|
|
5
|
+
|
|
6
|
+
## Development setup (source only)
|
|
7
|
+
|
|
8
|
+
There is no wheel/PyPI release yet. Work from the source tree:
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
python -m venv .venv
|
|
12
|
+
. .venv/bin/activate
|
|
13
|
+
pip install pytest ruff pip-audit PyYAML huggingface_hub fastapi httpx pydantic numpy
|
|
14
|
+
# for real inference:
|
|
15
|
+
pip install onnxruntime tokenizers
|
|
16
|
+
PYTHONPATH=src pytest -q
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The fast suite must not download models. Integration tests are opt-in:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
PYTHONPATH=src pytest -m integration -o addopts="" -s
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`tests/test_integration_gliner2.py` uses `DECISION_TOOLKIT_GLINER2_DIR` and
|
|
26
|
+
never downloads.
|
|
27
|
+
|
|
28
|
+
## Code rules
|
|
29
|
+
|
|
30
|
+
- Keep inference logic in adapters; the CLI and REST call the core.
|
|
31
|
+
- The core is the superset. Do not add wire-specific concepts to it.
|
|
32
|
+
- The REST layer may only project core capabilities; if a standard needs
|
|
33
|
+
something, add it to the core first.
|
|
34
|
+
- Every new error gets a stable `code` and a test for its HTTP/CLI mapping.
|
|
35
|
+
- No silent truncation: mark `truncated`/`warnings` or fail in strict mode.
|
|
36
|
+
- Logs carry model/variant/request id, not state/question content by default.
|
|
37
|
+
|
|
38
|
+
## Model admission
|
|
39
|
+
|
|
40
|
+
A model enters `CATALOG` only when it has:
|
|
41
|
+
|
|
42
|
+
1. a clean commercial license;
|
|
43
|
+
2. a real public ONNX export;
|
|
44
|
+
3. an adapter contract implemented from that export;
|
|
45
|
+
4. a **pinned commit SHA** and SHA256 for every declared file;
|
|
46
|
+
5. measured parity against a named baseline;
|
|
47
|
+
6. CPU latency measured on a known machine;
|
|
48
|
+
7. a model card and reproducibility commands.
|
|
49
|
+
|
|
50
|
+
Models that fail any gate belong in `docs/ROADMAP.md`, not in the catalog.
|
|
51
|
+
|
|
52
|
+
## Bench evidence
|
|
53
|
+
|
|
54
|
+
Re-run the harness when a model, adapter or quantized build changes:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
PYTHONPATH=src python bench/run.py --suite bench/suites/smoke.jsonl \
|
|
58
|
+
--repeats 3 --output bench/results/YYYY-MM-DD-smoke.json
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Do not overwrite an old result with a different machine silently; add a new
|
|
62
|
+
dated file and record the environment. Latency is post-warmup and is not the
|
|
63
|
+
same metric as the historical admission values in the model cards.
|
|
64
|
+
|
|
65
|
+
## Tests and lint
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
ruff check src tests bench
|
|
69
|
+
PYTHONPATH=src pytest -q
|
|
70
|
+
PYTHONPATH=src pytest -q -m fuzz # malformed REST corpus
|
|
71
|
+
python -m decision_toolkit --version
|
|
72
|
+
pip-audit # dependency scan (CI runs this)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
CI runs these on Python 3.12 and 3.13 from source, plus `pip-audit`. It does
|
|
76
|
+
**not** build a wheel; packaging is a later milestone. See
|
|
77
|
+
[OPERATIONS.md](docs/OPERATIONS.md) for rate limits, metrics, tracing and the
|
|
78
|
+
load probe.
|
|
79
|
+
|
|
80
|
+
## Commits
|
|
81
|
+
|
|
82
|
+
Use a short imperative subject. Mention the layer (`core`, `rest`, `cli`,
|
|
83
|
+
`repro`, `docs`) and whether catalog evidence changed.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 GIA-UH (Universidad de La Habana) and Syalia S.R.L.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: decision-toolkit
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local-first toolkit for open-weights decision models: one ONNX engine, one adapter per model contract, typed choice/yes-no/score decisions with probabilities on CPU.
|
|
5
|
+
Project-URL: Homepage, https://github.com/gia-uh/decision-toolkit
|
|
6
|
+
Project-URL: Repository, https://github.com/gia-uh/decision-toolkit
|
|
7
|
+
Project-URL: Issues, https://github.com/gia-uh/decision-toolkit/issues
|
|
8
|
+
Project-URL: Documentation, https://github.com/gia-uh/decision-toolkit/tree/main/docs
|
|
9
|
+
Project-URL: Changelog, https://github.com/gia-uh/decision-toolkit/blob/main/CHANGELOG.md
|
|
10
|
+
Author: Syalia S.R.L.
|
|
11
|
+
Author-email: Yudivián Almeida Cruz <yudivian@gmail.com>
|
|
12
|
+
License-Expression: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: classification,cpu,decision-models,jev,local-first,onnx,routing,system-one
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Requires-Python: >=3.12
|
|
25
|
+
Requires-Dist: huggingface-hub>=0.23
|
|
26
|
+
Requires-Dist: pyyaml>=6.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: httpx>=0.27; extra == 'dev'
|
|
29
|
+
Requires-Dist: openai>=1.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: pip-audit>=2; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8; extra == 'dev'
|
|
32
|
+
Requires-Dist: ruff>=0.6; extra == 'dev'
|
|
33
|
+
Provides-Extra: onnx
|
|
34
|
+
Requires-Dist: numpy>=1.26; extra == 'onnx'
|
|
35
|
+
Requires-Dist: onnxruntime>=1.18; extra == 'onnx'
|
|
36
|
+
Requires-Dist: tokenizers>=0.19; extra == 'onnx'
|
|
37
|
+
Provides-Extra: server
|
|
38
|
+
Requires-Dist: fastapi>=0.115; extra == 'server'
|
|
39
|
+
Requires-Dist: python-multipart>=0.0.9; extra == 'server'
|
|
40
|
+
Requires-Dist: uvicorn[standard]>=0.30; extra == 'server'
|
|
41
|
+
Provides-Extra: tracing
|
|
42
|
+
Requires-Dist: opentelemetry-api>=1.25; extra == 'tracing'
|
|
43
|
+
Description-Content-Type: text/markdown
|
|
44
|
+
|
|
45
|
+
# decision-toolkit
|
|
46
|
+
|
|
47
|
+
Local-first toolkit around **open-weights decision models**: typed **choice /
|
|
48
|
+
yes-no (`noul`) / score** questions over a state (text or JSON), answered with a
|
|
49
|
+
**calibrated probability for every option** — no generated text, no parsing.
|
|
50
|
+
|
|
51
|
+
One **ONNX engine** (`onnxruntime`), in-process, on CPU. One **adapter per model
|
|
52
|
+
contract** (how a model is fed and read). Three thin surfaces over the same API:
|
|
53
|
+
**library**, **CLI** and **REST**.
|
|
54
|
+
|
|
55
|
+
Co-developed by [GIA-UH](https://github.com/gia-uh) and [Syalia S.R.L.](https://github.com/syalia-srl).
|
|
56
|
+
|
|
57
|
+
**Examples:** [docs/EXAMPLES.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/EXAMPLES.md) covers the library, REST and
|
|
58
|
+
CLI. `dtk --help` and every subcommand help also include runnable examples.
|
|
59
|
+
|
|
60
|
+
## Installation
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install decision-toolkit # library + CLI (once v0.1.0 is on PyPI)
|
|
64
|
+
pip install "decision-toolkit[onnx]" # + onnxruntime, tokenizers (the model runtime)
|
|
65
|
+
pip install "decision-toolkit[server]" # + REST server (FastAPI)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Pre-release: the artifact is built and verified locally, but not uploaded yet.
|
|
69
|
+
See [docs/RELEASE.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/RELEASE.md) for the Trusted Publishing flow and
|
|
70
|
+
clean-install checks.
|
|
71
|
+
|
|
72
|
+
## Three ways to use it
|
|
73
|
+
|
|
74
|
+
**1. As a library (primary interface)**
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
from decision_toolkit import DecisionModel, choice, noul, score
|
|
78
|
+
|
|
79
|
+
# download once: dtk models download gliner2.5-multi
|
|
80
|
+
model = DecisionModel("gliner2.5-multi")
|
|
81
|
+
|
|
82
|
+
answer = model.decide(
|
|
83
|
+
state="Meeting with Acme: we agreed a 10% discount and will sign next week.",
|
|
84
|
+
questions={
|
|
85
|
+
"cat": choice("What kind of record is this?", {
|
|
86
|
+
"meeting": "a meeting or call with topics, decisions and actions",
|
|
87
|
+
"tasks": "things to do, with dates or owners",
|
|
88
|
+
"contact": "a person's details: name, role, company, phone, email",
|
|
89
|
+
"idea": "an idea, reflection, observation or question",
|
|
90
|
+
"other": "a greeting, small talk or anything else",
|
|
91
|
+
}),
|
|
92
|
+
"urgent": noul("Does it need a reply today?"),
|
|
93
|
+
},
|
|
94
|
+
)
|
|
95
|
+
answer["cat"].choice # 'meeting'
|
|
96
|
+
answer["cat"].confidence # 0.77
|
|
97
|
+
answer["urgent"].noul # probability of "true"
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
verdict = model.classify(
|
|
102
|
+
"Remind me: dentist next Tuesday at 9 am.",
|
|
103
|
+
{"reminder": "an event with a date or time",
|
|
104
|
+
"task": "a list of things to do",
|
|
105
|
+
"idea": "an idea or reflection"},
|
|
106
|
+
threshold=0.6,
|
|
107
|
+
)
|
|
108
|
+
verdict.label, verdict.confidence, verdict.below_threshold
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
The core is the **superset** of the surfaces. It also exposes lifecycle,
|
|
112
|
+
usage, batch/async, capabilities and structured events:
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
from decision_toolkit import DecisionModel, request_context, noul
|
|
116
|
+
|
|
117
|
+
with DecisionModel("gliner2.5-multi", strict=True) as model:
|
|
118
|
+
model.warmup()
|
|
119
|
+
with request_context("req-42"):
|
|
120
|
+
decision = model.decide("Customer wants a refund today.",
|
|
121
|
+
{"urgent": noul("Does it need a reply today?")})
|
|
122
|
+
batch = model.decide_many(["first state", "second state"],
|
|
123
|
+
{"urgent": noul("Today?")})
|
|
124
|
+
|
|
125
|
+
decision["urgent"].confidence # normalized probability
|
|
126
|
+
decision.usage.to_dict() # input/output tokens + estimated flag
|
|
127
|
+
decision.latency_ms # per-decision latency
|
|
128
|
+
decision.request_id # 'req-42'
|
|
129
|
+
decision.truncated, decision.warnings
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`strict=True` turns silent truncation into `TruncationError`. Capabilities are
|
|
133
|
+
validated before inference (`box`/assets are rejected by text-only models).
|
|
134
|
+
Custom adapters can be registered with `register_adapter`; local ONNX models
|
|
135
|
+
can be opened with `DecisionModel.from_onnx(...)`. See
|
|
136
|
+
[docs/CORE.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/CORE.md) for the full phase 1 contract, error taxonomy,
|
|
137
|
+
logging events and the expressiveness inventory.
|
|
138
|
+
|
|
139
|
+
**2. As a CLI**
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
dtk models list --adapter --quant # catalog, variants and cache state
|
|
143
|
+
dtk models variants gliner2.5-multi --json # real variants (quant, size, role)
|
|
144
|
+
dtk models download gliner2.5-multi --dry-run # show the download first
|
|
145
|
+
dtk models use gliner2.5-multi --variant fp16
|
|
146
|
+
dtk models current --json # includes the current variant
|
|
147
|
+
dtk decide --model gliner2.5-multi \
|
|
148
|
+
--state "Meeting with Acme: 10% discount, sign next week." \
|
|
149
|
+
--questions-file questions.yaml --json
|
|
150
|
+
dtk decide --model gliner2.5-multi \
|
|
151
|
+
--input-jsonl states.jsonl --questions-file questions.yaml \
|
|
152
|
+
--output-jsonl decisions.jsonl --batch-size 32
|
|
153
|
+
dtk classify --model gliner2.5-multi \
|
|
154
|
+
--text "Dentist next Tuesday at 9" --labels "reminder,task,idea" \
|
|
155
|
+
--threshold 0.6 --json
|
|
156
|
+
dtk config list --json
|
|
157
|
+
dtk models verify --all --json
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`dtk` has consistent exit codes (`0` ok, `1` runtime, `2` usage, `3`
|
|
161
|
+
verification), `--json` everywhere, quiet/verbose modes and no tracebacks for
|
|
162
|
+
expected failures. See [docs/CLI.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/CLI.md) for questions files, JSONL
|
|
163
|
+
batch, custom `--adapter/--files`, config and completion.
|
|
164
|
+
|
|
165
|
+
**3. As a REST service (optional extra)**
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
pip install "decision-toolkit[server]"
|
|
169
|
+
dtk serve --model gliner2.5-multi --warmup \
|
|
170
|
+
--api-key "$DECISION_TOOLKIT_API_KEY" \
|
|
171
|
+
--log-format json
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Endpoints: `POST /v1/systemone` (System One 1.14), `POST /v1/decisions`
|
|
175
|
+
(OpenAI Decisions beta), `GET /v1/models` (OpenAI shape),
|
|
176
|
+
`GET /v1/decision-models` (rich catalog), `GET /v1/info`, `GET /health`,
|
|
177
|
+
`GET /ready`. Requests may select `model` and `variant`; failures use the OpenAI
|
|
178
|
+
`{"error": {...}}` envelope and every response carries `x-request-id`.
|
|
179
|
+
`TYPESAFE_BASE_URL=http://localhost:8000` makes existing System One clients work.
|
|
180
|
+
See [docs/REST.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/REST.md) for the pinned contracts, limits, auth and
|
|
181
|
+
the standard-evolution process.
|
|
182
|
+
|
|
183
|
+
## The catalog is curated
|
|
184
|
+
|
|
185
|
+
The catalog is **ours**, not a mirror of HuggingFace. A model enters only when it
|
|
186
|
+
is understood and verified:
|
|
187
|
+
|
|
188
|
+
1. clean commercial license;
|
|
189
|
+
2. an adapter whose contract we can implement **from a real export**;
|
|
190
|
+
3. a real ONNX export on the Hub;
|
|
191
|
+
4. measured parity against the original on a fixed sample;
|
|
192
|
+
5. CPU-viable latency.
|
|
193
|
+
|
|
194
|
+
Every variant points to files that exist. No placeholders, no speculative
|
|
195
|
+
adapters (there is no generic "any ONNX" adapter).
|
|
196
|
+
|
|
197
|
+
| id | adapter | status | default variant |
|
|
198
|
+
| --- | --- | --- | --- |
|
|
199
|
+
| `gliner2.5-multi` | `gliner2` | **m0** — verified end-to-end (9/15, 353 ms) | `fp16` (577 MB) |
|
|
200
|
+
| `gliner2.5-decide` | `gliner2-decide` | **m0** — fused export verified (refund 0.944, 912 ms; English-only) | `q4f16` (524 MB) |
|
|
201
|
+
| `laya` | `laya` | **m0** — verified against torch (Δp ≤ 0.005) | `q8` (528 MB) |
|
|
202
|
+
| `julia-1` | `julia` | **m0** — verified: 100/100 argmax parity, 329 ms | `fp32` (618 MB) |
|
|
203
|
+
| `opendecider-nano` | `opendecider` | **m0** — official build; prompt ported from pinned code (530 ms) | `q8f16` (469 MB) |
|
|
204
|
+
|
|
205
|
+
**Only `m0` models are in the catalog.** Researched-but-unavailable models
|
|
206
|
+
(planned, conversion pending, broken exports) are listed in
|
|
207
|
+
[docs/ROADMAP.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/ROADMAP.md) and are not downloadable through the toolkit.
|
|
208
|
+
|
|
209
|
+
Per-model cards with the contract, measured evidence and known limitations:
|
|
210
|
+
[docs/models](https://github.com/gia-uh/decision-toolkit/tree/main/docs/models).
|
|
211
|
+
|
|
212
|
+
## Variants and quantization
|
|
213
|
+
|
|
214
|
+
A quantization is a **variant** of a model, with its real repo, files and role:
|
|
215
|
+
|
|
216
|
+
```yaml
|
|
217
|
+
gliner2.5-multi:
|
|
218
|
+
fp16: encoder_fp16.onnx + classifier_fp32.onnx # 577 MB default
|
|
219
|
+
fp32: encoder_fp32.onnx + classifier_fp32.onnx # 1132 MB quality
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- `dtk models download <id>` gets the **default** variant; `--variant fp32` gets
|
|
223
|
+
another.
|
|
224
|
+
- A variant becomes default only after passing the parity gate (top-1 ≥ 99%,
|
|
225
|
+
mean |Δp| ≤ 0.02, accuracy delta ≤ 1 pp against the fp32 build on a fixed
|
|
226
|
+
sample) and measuring latency on CPU.
|
|
227
|
+
- Unverified variants are never default. No silent fallback.
|
|
228
|
+
|
|
229
|
+
## Operations
|
|
230
|
+
|
|
231
|
+
The server has bounded inference concurrency (`--max-concurrent`), an optional
|
|
232
|
+
per-key rate limiter (`--rate-limit`, 429 + `Retry-After`), Prometheus metrics
|
|
233
|
+
at `/metrics`, and optional OpenTelemetry spans (`--tracing`). Saturation
|
|
234
|
+
returns 503 `server_busy` instead of building an unbounded queue. State and
|
|
235
|
+
question text never become log/metric labels.
|
|
236
|
+
|
|
237
|
+
```bash
|
|
238
|
+
dtk serve --model gliner2.5-multi --warmup --max-concurrent 4 \
|
|
239
|
+
--rate-limit 120 --rate-window 60
|
|
240
|
+
PYTHONPATH=src python bench/load.py --base-url http://127.0.0.1:8000 \
|
|
241
|
+
--concurrency 10 --requests 30
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
See [docs/OPERATIONS.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/OPERATIONS.md) for metric names, alerts, the
|
|
245
|
+
runbook and the multi-worker caveat.
|
|
246
|
+
|
|
247
|
+
## Reproducibility
|
|
248
|
+
|
|
249
|
+
Every catalog variant is pinned to a real HuggingFace **commit SHA** and carries
|
|
250
|
+
a **SHA256 per file**, including external `.onnx.data` files. Downloads reject
|
|
251
|
+
mismatches and verification treats the catalog hashes as the source of truth:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
dtk models verify --all --json # exit 3 if any file/hash/session fails
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
Latency/parity/calibration evidence is reproducible with the harness in
|
|
258
|
+
[`bench/`](https://github.com/gia-uh/decision-toolkit/tree/main/bench) and documented in [docs/BENCH.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/BENCH.md). CI runs
|
|
259
|
+
lint, unit/contract tests and a CLI smoke from source on Python 3.12/3.13.
|
|
260
|
+
|
|
261
|
+
There is **no wheel or PyPI release yet**; packaging is a later milestone. See
|
|
262
|
+
[CHANGELOG.md](https://github.com/gia-uh/decision-toolkit/blob/main/CHANGELOG.md), [CONTRIBUTING.md](https://github.com/gia-uh/decision-toolkit/blob/main/CONTRIBUTING.md),
|
|
263
|
+
[SECURITY.md](https://github.com/gia-uh/decision-toolkit/blob/main/SECURITY.md) and
|
|
264
|
+
[THIRD_PARTY_NOTICES.md](https://github.com/gia-uh/decision-toolkit/blob/main/THIRD_PARTY_NOTICES.md).
|
|
265
|
+
|
|
266
|
+
## Custom ONNX (at your own risk)
|
|
267
|
+
|
|
268
|
+
You can run any ONNX file with an adapter you trust — outside the curated
|
|
269
|
+
catalog:
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
model = DecisionModel.from_onnx(
|
|
273
|
+
"./my-model",
|
|
274
|
+
adapter="gliner2", # the layout you declare
|
|
275
|
+
files={"encoder": "encoder.onnx", "classifier": "classifier.onnx",
|
|
276
|
+
"tokenizer": "tokenizer.json"},
|
|
277
|
+
)
|
|
278
|
+
# no parity, no license check, no support
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Third-party layouts can be registered in-process:
|
|
282
|
+
|
|
283
|
+
```python
|
|
284
|
+
from decision_toolkit import register_adapter
|
|
285
|
+
register_adapter("my-layout", MyAdapter)
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Custom models do not appear in `dtk models list`; the toolkit never guesses a
|
|
289
|
+
layout. If a model deserves support, it goes through curation.
|
|
290
|
+
|
|
291
|
+
## Development
|
|
292
|
+
|
|
293
|
+
```bash
|
|
294
|
+
python -m venv .venv && . .venv/bin/activate
|
|
295
|
+
pip install pytest ruff PyYAML huggingface_hub fastapi httpx pydantic numpy
|
|
296
|
+
PYTHONPATH=src pytest -q # fast suite (no model downloads)
|
|
297
|
+
PYTHONPATH=src pytest -m integration -o addopts="" # real model, opt-in
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
No wheel or editable install is required while the project is pre-distribution.
|
|
301
|
+
CI runs the same source-tree commands on Python 3.12/3.13. See
|
|
302
|
+
[CONTRIBUTING.md](https://github.com/gia-uh/decision-toolkit/blob/main/CONTRIBUTING.md).
|
|
303
|
+
|
|
304
|
+
## License
|
|
305
|
+
|
|
306
|
+
MIT — see [LICENSE](https://github.com/gia-uh/decision-toolkit/blob/main/LICENSE).
|