decision-toolkit 0.3.1__tar.gz → 0.4.1__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.
Files changed (98) hide show
  1. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/CHANGELOG.md +27 -1
  2. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/PKG-INFO +11 -8
  3. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/README.md +10 -7
  4. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/CLI.md +3 -0
  5. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/EXAMPLES.md +3 -3
  6. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/RELEASE.md +12 -8
  7. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/REST.md +19 -19
  8. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/VARIANTS.md +32 -27
  9. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/gliner2.5-decide.md +34 -0
  10. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/gliner2.5-multi.md +63 -0
  11. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/julia-1.md +34 -0
  12. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/laya-multilingual.md +63 -0
  13. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/laya.md +34 -0
  14. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/open-jev-deberta-v3-large.md +121 -0
  15. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/opendecider-nano.md +63 -0
  16. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/strands-decider-2b.md +63 -0
  17. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/pyproject.toml +1 -1
  18. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/__init__.py +1 -1
  19. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/catalog.py +19 -0
  20. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/cli.py +4 -1
  21. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/contracts/openai_decisions_beta.py +1 -1
  22. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/contracts/systemone_v1.py +0 -1
  23. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/registry.py +8 -0
  24. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/server.py +69 -16
  25. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_cli.py +3 -0
  26. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_server.py +52 -8
  27. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/uv.lock +1 -1
  28. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/.github/workflows/ci.yml +0 -0
  29. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/.github/workflows/docs.yml +0 -0
  30. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/.github/workflows/release.yml +0 -0
  31. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/.gitignore +0 -0
  32. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/CONTRIBUTING.md +0 -0
  33. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/LICENSE +0 -0
  34. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/SECURITY.md +0 -0
  35. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/THIRD_PARTY_NOTICES.md +0 -0
  36. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/README.md +0 -0
  37. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/__init__.py +0 -0
  38. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/load.py +0 -0
  39. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/results/2026-10-20-calibration.json +0 -0
  40. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/results/2026-10-20-load.json +0 -0
  41. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/results/2026-10-20-smoke.json +0 -0
  42. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/run.py +0 -0
  43. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/suites/calibration.jsonl +0 -0
  44. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/bench/suites/smoke.jsonl +0 -0
  45. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/BENCH.md +0 -0
  46. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/CATALOG_PROPOSAL.md +0 -0
  47. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/CORE.md +0 -0
  48. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/OPERATIONS.md +0 -0
  49. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/ROADMAP.md +0 -0
  50. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/index.md +0 -0
  51. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/docs/models/README.md +0 -0
  52. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/__main__.py +0 -0
  53. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/__init__.py +0 -0
  54. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/base.py +0 -0
  55. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/gliner2.py +0 -0
  56. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/gliner2_decide.py +0 -0
  57. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/julia.py +0 -0
  58. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/laya.py +0 -0
  59. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/opendecider.py +0 -0
  60. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/openjev.py +0 -0
  61. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/adapters/strands_decider.py +0 -0
  62. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/calibration.py +0 -0
  63. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/config.py +0 -0
  64. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/contracts/__init__.py +0 -0
  65. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/contracts/common.py +0 -0
  66. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/contracts/mapping.py +0 -0
  67. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/decision_model.py +0 -0
  68. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/engine/__init__.py +0 -0
  69. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/errors.py +0 -0
  70. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/limits.py +0 -0
  71. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/metrics.py +0 -0
  72. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/models.py +0 -0
  73. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/observability.py +0 -0
  74. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/py.typed +0 -0
  75. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/results.py +0 -0
  76. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/schema.py +0 -0
  77. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/src/decision_toolkit/tracing.py +0 -0
  78. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/__init__.py +0 -0
  79. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/conftest.py +0 -0
  80. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_adapters_gliner2.py +0 -0
  81. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_adapters_gliner2_decide.py +0 -0
  82. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_adapters_julia.py +0 -0
  83. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_adapters_laya.py +0 -0
  84. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_adapters_opendecider.py +0 -0
  85. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_adapters_openjev.py +0 -0
  86. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_adapters_strands.py +0 -0
  87. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_bench.py +0 -0
  88. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_catalog.py +0 -0
  89. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_config.py +0 -0
  90. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_contracts_conformance.py +0 -0
  91. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_core_phase1.py +0 -0
  92. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_decision_model.py +0 -0
  93. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_fuzz_rest.py +0 -0
  94. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_integration_gliner2.py +0 -0
  95. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_models.py +0 -0
  96. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_operations.py +0 -0
  97. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_results.py +0 -0
  98. {decision_toolkit-0.3.1 → decision_toolkit-0.4.1}/tests/test_schema.py +0 -0
@@ -1,8 +1,32 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project are documented here. Version `0.3.1` is
3
+ All notable changes to this project are documented here. Version `0.4.1` is
4
4
  published on PyPI.
5
5
 
6
+ ## [0.4.1] — 2026-10-21
7
+
8
+ Catalog presentation patch: every variant now carries its REST alias
9
+ (`id@variant`) in `dtk models list`, `--json` and `dtk models variants`. Model
10
+ cards include copy-paste loading examples for library, CLI and REST for every
11
+ variant. No model or runtime changes.
12
+
13
+ ## [0.4.0] — 2026-10-21
14
+
15
+ REST variant-selection correction. Quantizations are selected through standard
16
+ model aliases (`id@variant`), not a non-standard `variant` body field. The same
17
+ REST process can load two variants of one base model in separate registry slots.
18
+
19
+ ### Fixed
20
+
21
+ - Removed `variant` from System One and OpenAI request bodies; standard clients
22
+ now see only the public request fields.
23
+ - Added `catalog-id@variant` model aliases to `/v1/models` and
24
+ `/v1/models/{id}`. The standard `model` field selects the alias.
25
+ - Registry keys and slots are per alias, so `laya-multilingual@fp16` and
26
+ `laya-multilingual@fp32` load independently and concurrently.
27
+ - Updated README, REST, variants, examples and CLI help so REST never shows a
28
+ variant field in a standard request body.
29
+
6
30
  ## [0.3.1] — 2026-10-21
7
31
 
8
32
  Catalog UX patch: `dtk models list` now prints one row per variant, with
@@ -192,6 +216,8 @@ README correction. No code or model behavior changed.
192
216
  - Typed `DecisionModel` API with lifecycle, batching, async, usage, warnings,
193
217
  normalized confidence, capabilities, structured events and atomic model store.
194
218
 
219
+ [0.4.1]: https://pypi.org/project/decision-toolkit/0.4.1/
220
+ [0.4.0]: https://pypi.org/project/decision-toolkit/0.4.0/
195
221
  [0.3.1]: https://pypi.org/project/decision-toolkit/0.3.1/
196
222
  [0.3.0]: https://pypi.org/project/decision-toolkit/0.3.0/
197
223
  [0.2.1]: https://pypi.org/project/decision-toolkit/0.2.1/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: decision-toolkit
3
- Version: 0.3.1
3
+ Version: 0.4.1
4
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
5
  Project-URL: Homepage, https://github.com/gia-uh/decision-toolkit
6
6
  Project-URL: Repository, https://github.com/gia-uh/decision-toolkit
@@ -59,7 +59,7 @@ and CLI. `dtk --help` and every subcommand help also include runnable examples.
59
59
  ## Installation
60
60
 
61
61
  ```bash
62
- uv pip install decision-toolkit # library + CLI (0.3.1 on PyPI)
62
+ uv pip install decision-toolkit # library + CLI (0.4.1 on PyPI)
63
63
  uv pip install "decision-toolkit[onnx]" # + onnxruntime, tokenizers (the model runtime)
64
64
  uv pip install "decision-toolkit[server]" # + REST server (FastAPI)
65
65
  ```
@@ -67,7 +67,7 @@ uv pip install "decision-toolkit[server]" # + REST server (FastAPI)
67
67
  The project is managed with [uv](https://docs.astral.sh/uv/) and ships a committed `uv.lock`. Published
68
68
  wheels remain compatible with `pip` for environments that do not use uv.
69
69
 
70
- PyPI: <https://pypi.org/project/decision-toolkit/0.3.1/>. See
70
+ PyPI: <https://pypi.org/project/decision-toolkit/0.4.1/>. See
71
71
  [docs/RELEASE.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/RELEASE.md)
72
72
  for the Trusted Publishing flow and clean-install checks.
73
73
 
@@ -211,6 +211,9 @@ adapters (there is no generic "any ONNX" adapter).
211
211
  | `open-jev-deberta-v3-large` | `open-jev` | **m0** — Open-Jev DeBERTa v3 large repro; 0.854 in-domain (241 ms) | `fp16`* (884 MB); `fp32` (1758 MB); `q4` (487 MB); `q4f16` (357 MB) |
212
212
  | `strands-decider-2b` | `strands-decider` | **m0** — reference fixture match (719 ms) | `q4f16`* (1114 MB); `q8` (1821 MB) |
213
213
 
214
+ Each variant has a REST alias in the form `id@variant`; `dtk models list` prints
215
+ the alias next to every variant.
216
+
214
217
  **Only `m0` models are in the catalog.** Researched-but-unavailable models
215
218
  (planned, conversion pending, broken exports) are listed in
216
219
  [docs/ROADMAP.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/ROADMAP.md) and are not downloadable through the toolkit.
@@ -248,11 +251,11 @@ dtk models current --json # {"current": "...", "variant": "fp32"}
248
251
  dtk decide --model gliner2.5-multi --variant fp16 --state "..." --q 'cat=choice:a|b'
249
252
  ```
250
253
 
251
- From Python: `DecisionModel("gliner2.5-multi", variant="fp32")`. REST selects
252
- the same way with `"variant": "fp32"` in the request body and returns the choice
253
- under `x_decision_toolkit.variant`. The variant must already be downloaded. If a
254
- variant is omitted, the catalog default is used unless `dtk models use` selected
255
- another one.
254
+ From Python: `DecisionModel("gliner2.5-multi", variant="fp32")`. In REST,
255
+ quantizations are **model aliases**: use `"model": "gliner2.5-multi@fp32"` in the
256
+ standard `model` field. The base id uses the server/persisted default. There is
257
+ no `variant` field in the request body; `/v1/models` lists both the base model
258
+ and every alias, and two aliases can be loaded concurrently.
256
259
 
257
260
  A variant becomes default only after passing the parity gate (top-1 ≥ 99%,
258
261
  mean |Δp| ≤ 0.02, accuracy delta ≤ 1 pp against the fp32 build on a fixed
@@ -21,7 +21,7 @@ and CLI. `dtk --help` and every subcommand help also include runnable examples.
21
21
  ## Installation
22
22
 
23
23
  ```bash
24
- uv pip install decision-toolkit # library + CLI (0.3.1 on PyPI)
24
+ uv pip install decision-toolkit # library + CLI (0.4.1 on PyPI)
25
25
  uv pip install "decision-toolkit[onnx]" # + onnxruntime, tokenizers (the model runtime)
26
26
  uv pip install "decision-toolkit[server]" # + REST server (FastAPI)
27
27
  ```
@@ -29,7 +29,7 @@ uv pip install "decision-toolkit[server]" # + REST server (FastAPI)
29
29
  The project is managed with [uv](https://docs.astral.sh/uv/) and ships a committed `uv.lock`. Published
30
30
  wheels remain compatible with `pip` for environments that do not use uv.
31
31
 
32
- PyPI: <https://pypi.org/project/decision-toolkit/0.3.1/>. See
32
+ PyPI: <https://pypi.org/project/decision-toolkit/0.4.1/>. See
33
33
  [docs/RELEASE.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/RELEASE.md)
34
34
  for the Trusted Publishing flow and clean-install checks.
35
35
 
@@ -173,6 +173,9 @@ adapters (there is no generic "any ONNX" adapter).
173
173
  | `open-jev-deberta-v3-large` | `open-jev` | **m0** — Open-Jev DeBERTa v3 large repro; 0.854 in-domain (241 ms) | `fp16`* (884 MB); `fp32` (1758 MB); `q4` (487 MB); `q4f16` (357 MB) |
174
174
  | `strands-decider-2b` | `strands-decider` | **m0** — reference fixture match (719 ms) | `q4f16`* (1114 MB); `q8` (1821 MB) |
175
175
 
176
+ Each variant has a REST alias in the form `id@variant`; `dtk models list` prints
177
+ the alias next to every variant.
178
+
176
179
  **Only `m0` models are in the catalog.** Researched-but-unavailable models
177
180
  (planned, conversion pending, broken exports) are listed in
178
181
  [docs/ROADMAP.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/ROADMAP.md) and are not downloadable through the toolkit.
@@ -210,11 +213,11 @@ dtk models current --json # {"current": "...", "variant": "fp32"}
210
213
  dtk decide --model gliner2.5-multi --variant fp16 --state "..." --q 'cat=choice:a|b'
211
214
  ```
212
215
 
213
- From Python: `DecisionModel("gliner2.5-multi", variant="fp32")`. REST selects
214
- the same way with `"variant": "fp32"` in the request body and returns the choice
215
- under `x_decision_toolkit.variant`. The variant must already be downloaded. If a
216
- variant is omitted, the catalog default is used unless `dtk models use` selected
217
- another one.
216
+ From Python: `DecisionModel("gliner2.5-multi", variant="fp32")`. In REST,
217
+ quantizations are **model aliases**: use `"model": "gliner2.5-multi@fp32"` in the
218
+ standard `model` field. The base id uses the server/persisted default. There is
219
+ no `variant` field in the request body; `/v1/models` lists both the base model
220
+ and every alias, and two aliases can be loaded concurrently.
218
221
 
219
222
  A variant becomes default only after passing the parity gate (top-1 ≥ 99%,
220
223
  mean |Δp| ≤ 0.02, accuracy delta ≤ 1 pp against the fp32 build on a fixed
@@ -151,6 +151,9 @@ are already downloaded.
151
151
  | `open-jev-deberta-v3-large` | `fp16`*, `fp32`, `q4`, `q4f16` | fp16 / fp32 / q4 / q4-fp16 | 883.5 / 1757.9 / 486.7 / 357.0 MB |
152
152
  | `strands-decider-2b` | `q4f16`*, `q8` | q4-fp16 / int8 | 1113.6 / 1821.4 MB |
153
153
 
154
+ Each row is also addressable over REST as `model = "<id>@<variant>"`; the alias
155
+ is printed by `dtk models list` and by `dtk models variants <id>`.
156
+
154
157
  ```bash
155
158
  # inspect, including the exact download command for each variant
156
159
  dtk models list --quant
@@ -165,7 +165,8 @@ curl -s http://127.0.0.1:8000/v1/decisions \
165
165
  }' | jq
166
166
  ```
167
167
 
168
- `variant` is a decision-toolkit extension; omit it for a standard client.
168
+ Use the model alias `id@variant` for a specific quantization; the base id uses
169
+ the server's default. There is no `variant` field in the request body.
169
170
 
170
171
  ### Model per request and auth
171
172
 
@@ -176,8 +177,7 @@ curl -s http://127.0.0.1:8000/v1/systemone \
176
177
  -H "Authorization: Bearer secret" \
177
178
  -H "Content-Type: application/json" \
178
179
  -d '{
179
- "model": "gliner2.5-decide",
180
- "variant": "q4f16",
180
+ "model": "gliner2.5-multi@fp32",
181
181
  "state": "I want my money back.",
182
182
  "questions": {
183
183
  "intent": {"type": "choice", "instructions": "What is the request?",
@@ -10,7 +10,9 @@ and selection in the CLI and documentation; `0.1.7` aligned variant selection
10
10
  across the library, CLI and REST API; `0.2.0` corrected the REST conformance
11
11
  claims and wire shapes to the published TypeSafe and OpenAI schemas; `0.2.1`
12
12
  changed the public documentation URLs to this repository's Pages site; `0.3.0`
13
- added the Laya multilingual, Open-Jev DeBERTa v3 large and Strands Decider ONNX models; `0.3.1` made the catalog list one row per variant with exact commands.
13
+ added the Laya multilingual, Open-Jev DeBERTa v3 large and Strands Decider ONNX models; `0.3.1` made the catalog list one row per variant with exact commands; `0.4.0`
14
+ replaced the non-standard REST `variant` body field with standard model aliases
15
+ (`id@variant`).
14
16
 
15
17
  ## Local build and verification
16
18
 
@@ -37,7 +39,7 @@ Clean install test (no `PYTHONPATH`):
37
39
  ```bash
38
40
  uv venv --python 3.12 /tmp/dtk-wheel
39
41
  uv pip install --python /tmp/dtk-wheel/bin/python \
40
- dist/decision_toolkit-0.3.1-py3-none-any.whl
42
+ dist/decision_toolkit-0.4.1-py3-none-any.whl
41
43
  uv pip check --python /tmp/dtk-wheel/bin/python
42
44
  /tmp/dtk-wheel/bin/dtk --version
43
45
  /tmp/dtk-wheel/bin/dtk models list --json
@@ -48,7 +50,7 @@ With inference and server extras:
48
50
 
49
51
  ```bash
50
52
  uv pip install --python /tmp/dtk-wheel/bin/python \
51
- "dist/decision_toolkit-0.3.1-py3-none-any.whl[onnx,server,tracing]"
53
+ "dist/decision_toolkit-0.4.1-py3-none-any.whl[onnx,server,tracing]"
52
54
  /tmp/dtk-wheel/bin/dtk decide --model gliner2.5-decide ...
53
55
  /tmp/dtk-wheel/bin/dtk serve --model gliner2.5-decide --warmup --port 8016
54
56
  curl -s http://127.0.0.1:8016/health
@@ -82,8 +84,8 @@ No long-lived PyPI token is needed; the workflow uses GitHub OIDC
82
84
  4. Create and push an annotated tag:
83
85
 
84
86
  ```bash
85
- git tag -a v0.3.1 -m "decision-toolkit 0.3.1"
86
- git push origin v0.3.1
87
+ git tag -a v0.4.1 -m "decision-toolkit 0.4.1"
88
+ git push origin v0.4.1
87
89
  ```
88
90
 
89
91
  5. `.github/workflows/release.yml` builds once, publishes to **TestPyPI**, then
@@ -132,9 +134,9 @@ to TestPyPI without publishing to PyPI or creating a GitHub release.
132
134
 
133
135
  ## Published release
134
136
 
135
- `0.3.1` is published through Trusted Publishing on 2026-10-21:
137
+ `0.4.1` is published through Trusted Publishing on 2026-10-21:
136
138
 
137
- <https://pypi.org/project/decision-toolkit/0.3.1/>
139
+ <https://pypi.org/project/decision-toolkit/0.4.1/>
138
140
 
139
141
  `0.1.0` was the first release; `0.1.1` refreshed the PyPI description; `0.1.2`
140
142
  validated the OIDC TestPyPI → PyPI flow; `0.1.3` added post-publish install
@@ -144,7 +146,9 @@ published to TestPyPI only; `0.1.5` fixed TestPyPI resolution; `0.1.6` added
144
146
  complete variant/quantization visibility and CLI selection; `0.1.7` aligned
145
147
  selection across library, CLI and REST; `0.2.0` corrected the REST conformance
146
148
  claims and wire shapes; `0.2.1` changed the public documentation URLs; `0.3.0`
147
- added the three new ONNX decision models; `0.3.1` made the catalog list one row per variant.
149
+ added the three new ONNX decision models; `0.3.1` made the catalog list one row per variant; `0.4.0` moved REST variant
150
+ selection to standard model aliases; `0.4.1` exposed aliases in the catalog and
151
+ per-variant loading examples in every model card.
148
152
 
149
153
  The tag-triggered workflow remains the preferred path for future releases once
150
154
  the TestPyPI/PyPI trusted publishers and protected environments are configured.
@@ -49,25 +49,25 @@ limitations listed below.
49
49
  Toolkit-only metadata is under `x_decision_toolkit`, never mixed into standard
50
50
  fields. `/v1/decision-models` is the rich, non-standard catalog endpoint.
51
51
 
52
- ## Variants (local extension)
53
-
54
- Neither TypeSafe nor OpenAI has a `variant` request field: the public APIs select
55
- a model by name. Quantization is a **decision-toolkit extension**:
56
-
57
- - Request `variant` wins when a local client sends it.
58
- - Otherwise, a server started with `dtk serve --variant` uses that default.
59
- - Otherwise, the variant selected with `dtk models use` is used when the server
60
- is serving the current model.
61
- - Otherwise, the catalog default (the `*` variant in `dtk models list`) is used.
62
-
63
- Standard clients should omit `variant`; they work with the server default. A
64
- standard-only integration can run one server per variant, or use the server's
65
- `--variant` setting. The selected variant is exposed in the namespaced
66
- `x_decision_toolkit.variant` extension, never in a standard field.
67
- `/v1/models`, `/v1/models/{id}`, `/v1/decision-models`, `/v1/info` and `/ready`
68
- expose the configured catalog variants, the default, cached variants, and the
69
- current selection. See [Variants and quantization](VARIANTS.md) for the catalog
70
- and download/select commands.
52
+ ## Variants as model aliases
53
+
54
+ Neither TypeSafe nor OpenAI has a `variant` request field. The toolkit therefore
55
+ publishes each quantization as a **model alias**:
56
+
57
+ ```json
58
+ {"model": "gliner2.5-multi@fp32", "state": "...", "questions": {...}}
59
+ ```
60
+
61
+ The `model` field is the standard field. `gliner2.5-multi` uses the server's
62
+ default (or the persisted `dtk models use` selection); `gliner2.5-multi@fp32`
63
+ selects that exact variant. Both aliases are listed by `/v1/models`, and the
64
+ registry loads them in separate slots, so two variants of one base model can be
65
+ used by the same server process.
66
+
67
+ `/v1/models/gliner2.5-multi@fp32`, `/v1/decision-models`, `/v1/info` and `/ready`
68
+ expose the alias, variant, default, cached state and current selection. The
69
+ response metadata under `x_decision_toolkit.variant` is an extension; the
70
+ request body remains standard. See [Variants and quantization](VARIANTS.md).
71
71
 
72
72
  ## System One (TypeSafe API schema 0.2.0)
73
73
 
@@ -18,23 +18,23 @@ have not passed the catalog gates are not exposed as variants.
18
18
  A `*` marks the default variant. It is used when `--variant` is omitted and no
19
19
  other variant was selected with `dtk models use`.
20
20
 
21
- | Model | Variant | Quantization | Size | Role | Download |
22
- | --- | --- | --- | ---: | --- | --- |
23
- | `gliner2.5-multi` | `fp16` * | fp16 encoder + fp32 classifier | 577.1 MB | default | `dtk models download gliner2.5-multi --variant fp16` |
24
- | `gliner2.5-multi` | `fp32` | fp32 encoder + fp32 classifier | 1131.9 MB | quality | `dtk models download gliner2.5-multi --variant fp32` |
25
- | `gliner2.5-decide` | `q4f16` * | q4/fp16 | 523.5 MB | default | `dtk models download gliner2.5-decide --variant q4f16` |
26
- | `laya` | `q8` * | int8 | 527.9 MB | default | `dtk models download laya --variant q8` |
27
- | `julia-1` | `fp32` * | fp32 | 618.2 MB | default | `dtk models download julia-1 --variant fp32` |
28
- | `opendecider-nano` | `q8f16` * | int8/fp16 | 468.8 MB | default | `dtk models download opendecider-nano --variant q8f16` |
29
- | `opendecider-nano` | `fp16` | fp16 | 791.9 MB | quality | `dtk models download opendecider-nano --variant fp16` |
30
- | `laya-multilingual` | `fp16` * | fp16 | 681.8 MB | default | `dtk models download laya-multilingual --variant fp16` |
31
- | `laya-multilingual` | `fp32` | fp32 | 1325.6 MB | quality | `dtk models download laya-multilingual --variant fp32` |
32
- | `open-jev-deberta-v3-large` | `fp16` * | fp16 | 883.5 MB | default | `dtk models download open-jev-deberta-v3-large --variant fp16` |
33
- | `open-jev-deberta-v3-large` | `fp32` | fp32 | 1757.9 MB | quality | `dtk models download open-jev-deberta-v3-large --variant fp32` |
34
- | `open-jev-deberta-v3-large` | `q4` | q4 | 486.7 MB | edge | `dtk models download open-jev-deberta-v3-large --variant q4` |
35
- | `open-jev-deberta-v3-large` | `q4f16` | q4/fp16 | 357.0 MB | edge | `dtk models download open-jev-deberta-v3-large --variant q4f16` |
36
- | `strands-decider-2b` | `q4f16` * | q4/fp16 | 1113.6 MB | default | `dtk models download strands-decider-2b --variant q4f16` |
37
- | `strands-decider-2b` | `q8` | int8 | 1821.4 MB | quality | `dtk models download strands-decider-2b --variant q8` |
21
+ | Model | Variant | REST alias | Quantization | Size | Role | Download |
22
+ | --- | --- | --- | --- | ---: | --- | --- |
23
+ | `gliner2.5-multi` | `fp16` * | `gliner2.5-multi@fp16` | fp16 | 577.1 MB | default | `dtk models download gliner2.5-multi --variant fp16` |
24
+ | `gliner2.5-multi` | `fp32` | `gliner2.5-multi@fp32` | fp32 | 1131.9 MB | quality | `dtk models download gliner2.5-multi --variant fp32` |
25
+ | `gliner2.5-decide` | `q4f16` * | `gliner2.5-decide@q4f16` | q4/fp16 | 523.5 MB | default | `dtk models download gliner2.5-decide --variant q4f16` |
26
+ | `laya` | `q8` * | `laya@q8` | int8 | 527.9 MB | default | `dtk models download laya --variant q8` |
27
+ | `julia-1` | `fp32` * | `julia-1@fp32` | fp32 | 618.2 MB | default | `dtk models download julia-1 --variant fp32` |
28
+ | `opendecider-nano` | `q8f16` * | `opendecider-nano@q8f16` | int8/fp16 | 468.8 MB | default | `dtk models download opendecider-nano --variant q8f16` |
29
+ | `opendecider-nano` | `fp16` | `opendecider-nano@fp16` | fp16 | 791.9 MB | quality | `dtk models download opendecider-nano --variant fp16` |
30
+ | `laya-multilingual` | `fp16` * | `laya-multilingual@fp16` | fp16 | 681.8 MB | default | `dtk models download laya-multilingual --variant fp16` |
31
+ | `laya-multilingual` | `fp32` | `laya-multilingual@fp32` | fp32 | 1325.6 MB | quality | `dtk models download laya-multilingual --variant fp32` |
32
+ | `open-jev-deberta-v3-large` | `q4f16` | `open-jev-deberta-v3-large@q4f16` | q4/fp16 | 357.0 MB | edge | `dtk models download open-jev-deberta-v3-large --variant q4f16` |
33
+ | `open-jev-deberta-v3-large` | `fp16` * | `open-jev-deberta-v3-large@fp16` | fp16 | 883.5 MB | default | `dtk models download open-jev-deberta-v3-large --variant fp16` |
34
+ | `open-jev-deberta-v3-large` | `q4` | `open-jev-deberta-v3-large@q4` | q4 | 486.7 MB | edge | `dtk models download open-jev-deberta-v3-large --variant q4` |
35
+ | `open-jev-deberta-v3-large` | `fp32` | `open-jev-deberta-v3-large@fp32` | fp32 | 1757.9 MB | quality | `dtk models download open-jev-deberta-v3-large --variant fp32` |
36
+ | `strands-decider-2b` | `q4f16` * | `strands-decider-2b@q4f16` | q4/fp16 | 1113.6 MB | default | `dtk models download strands-decider-2b --variant q4f16` |
37
+ | `strands-decider-2b` | `q8` | `strands-decider-2b@q8` | int8 | 1821.4 MB | quality | `dtk models download strands-decider-2b --variant q8` |
38
38
 
39
39
  Models with a single row have only one catalogued, verified build. That does
40
40
  not mean other conversions do not exist upstream; it means they have not passed
@@ -45,8 +45,8 @@ the parity, hash and license gates for this catalog.
45
45
  The same catalog variant is used by the library, CLI and REST API. Selection
46
46
  precedence is:
47
47
 
48
- 1. an explicit per-call/per-request variant (`DecisionModel(..., variant=...)`,
49
- `--variant`, or `"variant"` in a REST body);
48
+ 1. an explicit per-call variant in the library or CLI
49
+ (`DecisionModel(..., variant=...)`, `--variant`);
50
50
  2. the CLI/server configuration (`dtk models use`, or `dtk serve --variant`);
51
51
  3. the catalog default marked with `*`.
52
52
 
@@ -54,17 +54,18 @@ precedence is:
54
54
  | --- | --- | --- |
55
55
  | Library | `DecisionModel("gliner2.5-multi", variant="fp32")` | persisted variant for the current model, else catalog default |
56
56
  | CLI | `--variant fp32` | variant selected by `dtk models use`, else catalog default |
57
- | REST (local extension) | `"variant": "fp32"` in the request body | server `--variant`, else persisted variant for the current model, else catalog default |
57
+ | REST | `"model": "gliner2.5-multi@fp32"` (model alias) | base model id; server `--variant`/persisted default |
58
58
 
59
59
  `DecisionModel.variant` and the CLI report the selected variant (`id@variant` in
60
60
  `dtk models current`). REST responses expose it under the namespaced
61
61
  `x_decision_toolkit.variant` extension; `/v1/decision-models` includes
62
62
  `current` and `current_variant`, plus all `variants` and `cached_variants`.
63
- `variant` is **not** part of TypeSafe or OpenAI; standard clients should omit it
64
- and rely on the server's `--variant`/persisted default. A server started with
63
+ `variant` is **not** part of TypeSafe or OpenAI, and standard REST clients must
64
+ not send it. They select only `model`; the local server chooses the quantization
65
+ from `--variant` or the persisted default. A server started with
65
66
  `--model`/`--variant` reports that default in `/v1/info` and `/ready`.
66
67
 
67
- The variant must have been downloaded before a library call or REST request
68
+ The variant must have been downloaded before a library call or REST alias
68
69
  loads it. An unknown variant returns a model-not-found error; a known but
69
70
  missing cache entry returns a model-not-downloaded error.
70
71
 
@@ -162,14 +163,18 @@ from decision_toolkit import DecisionModel
162
163
  model = DecisionModel("gliner2.5-multi", variant="fp32")
163
164
  ```
164
165
 
165
- From REST, send the variant in the request body:
166
+ For REST, select a quantization through the standard `model` field using the
167
+ alias `id@variant`:
166
168
 
167
169
  ```json
168
- {"model": "gliner2.5-multi", "variant": "fp32", "state": "...",
169
- "questions": {"cat": {"type": "choice", "instructions": "What kind?",
170
- "criteria": {"meeting": null, "other": null}}}}
170
+ {"model": "gliner2.5-multi@fp32", "state": "...", "questions": {}}
171
171
  ```
172
172
 
173
+ The base id (`gliner2.5-multi`) uses the server `--variant`/persisted default.
174
+ `/v1/models` lists base models and every alias, and the registry can hold two
175
+ aliases of the same base model in separate slots. Do not add a `variant` field
176
+ to the request body.
177
+
173
178
  The variant must already be downloaded. If you name an unknown variant, the
174
179
  error lists the catalogued choices, for example:
175
180
 
@@ -54,6 +54,40 @@ in the toolkit; see [Variants and quantization](../VARIANTS.md).
54
54
  - Quantized q4f16; the fp16/fp32 builds may resolve the sentiment example differently.
55
55
  - The model card's examples are "potential outputs", not guarantees; validate on your data.
56
56
 
57
+ ## Loading each variant
58
+
59
+ REST selects the quantization through the standard `model` field using the
60
+ alias `gliner2.5-decide@variant`; there is no `variant` field in the request body.
61
+
62
+ ### `q4f16` (q4/fp16, default)
63
+
64
+ **Library**
65
+
66
+ ```python
67
+ from decision_toolkit import DecisionModel
68
+
69
+ model = DecisionModel("gliner2.5-decide", variant="q4f16")
70
+ ```
71
+
72
+ **CLI**
73
+
74
+ ```bash
75
+ dtk models download gliner2.5-decide --variant q4f16
76
+ dtk models use gliner2.5-decide --variant q4f16
77
+ dtk decide --model gliner2.5-decide --variant q4f16 \
78
+ --state "..." --q 'urgent=noul:Is this urgent?'
79
+ ```
80
+
81
+ **REST**
82
+
83
+ ```bash
84
+ curl -s http://127.0.0.1:8000/v1/systemone \
85
+ -H "Content-Type: application/json" \
86
+ -d '{"model": "gliner2.5-decide@q4f16", "state": "...",
87
+ "questions": {"urgent": {"type": "noul",
88
+ "instructions": "Is this urgent?"}}}}'
89
+ ```
90
+
57
91
  ## Usage
58
92
 
59
93
  ```bash
@@ -55,6 +55,69 @@ makes it persistent. See [Variants and quantization](../VARIANTS.md).
55
55
  - English/multilingual tokenizer fallback: the run above used the raw `tokenizers` fallback; a `transformers` tokenizer may change scores slightly.
56
56
  - Benchmarked on a synthetic Spanish routing set; validate on your own data.
57
57
 
58
+ ## Loading each variant
59
+
60
+ REST selects the quantization through the standard `model` field using the
61
+ alias `gliner2.5-multi@variant`; there is no `variant` field in the request body.
62
+
63
+ ### `fp16` (fp16, default)
64
+
65
+ **Library**
66
+
67
+ ```python
68
+ from decision_toolkit import DecisionModel
69
+
70
+ model = DecisionModel("gliner2.5-multi", variant="fp16")
71
+ ```
72
+
73
+ **CLI**
74
+
75
+ ```bash
76
+ dtk models download gliner2.5-multi --variant fp16
77
+ dtk models use gliner2.5-multi --variant fp16
78
+ dtk decide --model gliner2.5-multi --variant fp16 \
79
+ --state "..." --q 'urgent=noul:Is this urgent?'
80
+ ```
81
+
82
+ **REST**
83
+
84
+ ```bash
85
+ curl -s http://127.0.0.1:8000/v1/systemone \
86
+ -H "Content-Type: application/json" \
87
+ -d '{"model": "gliner2.5-multi@fp16", "state": "...",
88
+ "questions": {"urgent": {"type": "noul",
89
+ "instructions": "Is this urgent?"}}}}'
90
+ ```
91
+
92
+ ### `fp32` (fp32, quality)
93
+
94
+ **Library**
95
+
96
+ ```python
97
+ from decision_toolkit import DecisionModel
98
+
99
+ model = DecisionModel("gliner2.5-multi", variant="fp32")
100
+ ```
101
+
102
+ **CLI**
103
+
104
+ ```bash
105
+ dtk models download gliner2.5-multi --variant fp32
106
+ dtk models use gliner2.5-multi --variant fp32
107
+ dtk decide --model gliner2.5-multi --variant fp32 \
108
+ --state "..." --q 'urgent=noul:Is this urgent?'
109
+ ```
110
+
111
+ **REST**
112
+
113
+ ```bash
114
+ curl -s http://127.0.0.1:8000/v1/systemone \
115
+ -H "Content-Type: application/json" \
116
+ -d '{"model": "gliner2.5-multi@fp32", "state": "...",
117
+ "questions": {"urgent": {"type": "noul",
118
+ "instructions": "Is this urgent?"}}}}'
119
+ ```
120
+
58
121
  ## Usage
59
122
 
60
123
  ```bash
@@ -55,6 +55,40 @@ exists but was not validated and is not catalogued; see
55
55
  - Historical benchmarks used 1024 tokens; accuracy at 8k is not established by the export.
56
56
  - The tokenizer uses `<mask>/<bos>/<eos>`; both conventions are handled, but this checkpoint's tokenizer is the `<...>` one.
57
57
 
58
+ ## Loading each variant
59
+
60
+ REST selects the quantization through the standard `model` field using the
61
+ alias `julia-1@variant`; there is no `variant` field in the request body.
62
+
63
+ ### `fp32` (fp32, default)
64
+
65
+ **Library**
66
+
67
+ ```python
68
+ from decision_toolkit import DecisionModel
69
+
70
+ model = DecisionModel("julia-1", variant="fp32")
71
+ ```
72
+
73
+ **CLI**
74
+
75
+ ```bash
76
+ dtk models download julia-1 --variant fp32
77
+ dtk models use julia-1 --variant fp32
78
+ dtk decide --model julia-1 --variant fp32 \
79
+ --state "..." --q 'urgent=noul:Is this urgent?'
80
+ ```
81
+
82
+ **REST**
83
+
84
+ ```bash
85
+ curl -s http://127.0.0.1:8000/v1/systemone \
86
+ -H "Content-Type: application/json" \
87
+ -d '{"model": "julia-1@fp32", "state": "...",
88
+ "questions": {"urgent": {"type": "noul",
89
+ "instructions": "Is this urgent?"}}}}'
90
+ ```
91
+
58
92
  ## Usage
59
93
 
60
94
  ```bash
@@ -68,6 +68,69 @@ the full-precision build. Select one with
68
68
  - The adapter uses the fused graph path; the English split checkpoint remains a
69
69
  separate catalog entry (`laya`).
70
70
 
71
+ ## Loading each variant
72
+
73
+ REST selects the quantization through the standard `model` field using the
74
+ alias `laya-multilingual@variant`; there is no `variant` field in the request body.
75
+
76
+ ### `fp16` (fp16, default)
77
+
78
+ **Library**
79
+
80
+ ```python
81
+ from decision_toolkit import DecisionModel
82
+
83
+ model = DecisionModel("laya-multilingual", variant="fp16")
84
+ ```
85
+
86
+ **CLI**
87
+
88
+ ```bash
89
+ dtk models download laya-multilingual --variant fp16
90
+ dtk models use laya-multilingual --variant fp16
91
+ dtk decide --model laya-multilingual --variant fp16 \
92
+ --state "..." --q 'urgent=noul:Is this urgent?'
93
+ ```
94
+
95
+ **REST**
96
+
97
+ ```bash
98
+ curl -s http://127.0.0.1:8000/v1/systemone \
99
+ -H "Content-Type: application/json" \
100
+ -d '{"model": "laya-multilingual@fp16", "state": "...",
101
+ "questions": {"urgent": {"type": "noul",
102
+ "instructions": "Is this urgent?"}}}}'
103
+ ```
104
+
105
+ ### `fp32` (fp32, quality)
106
+
107
+ **Library**
108
+
109
+ ```python
110
+ from decision_toolkit import DecisionModel
111
+
112
+ model = DecisionModel("laya-multilingual", variant="fp32")
113
+ ```
114
+
115
+ **CLI**
116
+
117
+ ```bash
118
+ dtk models download laya-multilingual --variant fp32
119
+ dtk models use laya-multilingual --variant fp32
120
+ dtk decide --model laya-multilingual --variant fp32 \
121
+ --state "..." --q 'urgent=noul:Is this urgent?'
122
+ ```
123
+
124
+ **REST**
125
+
126
+ ```bash
127
+ curl -s http://127.0.0.1:8000/v1/systemone \
128
+ -H "Content-Type: application/json" \
129
+ -d '{"model": "laya-multilingual@fp32", "state": "...",
130
+ "questions": {"urgent": {"type": "noul",
131
+ "instructions": "Is this urgent?"}}}}'
132
+ ```
133
+
71
134
  ## Usage
72
135
 
73
136
  ```bash
@@ -61,6 +61,40 @@ fp32 build; see [Variants and quantization](../VARIANTS.md).
61
61
  - **English checkpoint.** The multilingual checkpoint is a different model (mmBERT-base) and has no split public export yet.
62
62
  - q8 quantization; the fp32 build was not published in this repo.
63
63
 
64
+ ## Loading each variant
65
+
66
+ REST selects the quantization through the standard `model` field using the
67
+ alias `laya@variant`; there is no `variant` field in the request body.
68
+
69
+ ### `q8` (int8, default)
70
+
71
+ **Library**
72
+
73
+ ```python
74
+ from decision_toolkit import DecisionModel
75
+
76
+ model = DecisionModel("laya", variant="q8")
77
+ ```
78
+
79
+ **CLI**
80
+
81
+ ```bash
82
+ dtk models download laya --variant q8
83
+ dtk models use laya --variant q8
84
+ dtk decide --model laya --variant q8 \
85
+ --state "..." --q 'urgent=noul:Is this urgent?'
86
+ ```
87
+
88
+ **REST**
89
+
90
+ ```bash
91
+ curl -s http://127.0.0.1:8000/v1/systemone \
92
+ -H "Content-Type: application/json" \
93
+ -d '{"model": "laya@q8", "state": "...",
94
+ "questions": {"urgent": {"type": "noul",
95
+ "instructions": "Is this urgent?"}}}}'
96
+ ```
97
+
64
98
  ## Usage
65
99
 
66
100
  ```bash