decision-toolkit 0.1.7__tar.gz → 0.2.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 (90) hide show
  1. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/CHANGELOG.md +34 -2
  2. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/PKG-INFO +16 -12
  3. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/README.md +14 -10
  4. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/CORE.md +1 -1
  5. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/EXAMPLES.md +4 -0
  6. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/RELEASE.md +11 -8
  7. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/REST.md +90 -66
  8. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/VARIANTS.md +7 -5
  9. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/index.md +1 -1
  10. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/pyproject.toml +2 -2
  11. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/__init__.py +1 -1
  12. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/contracts/__init__.py +2 -2
  13. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/contracts/mapping.py +36 -12
  14. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/contracts/openai_decisions_beta.py +4 -8
  15. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/contracts/systemone_v1.py +43 -27
  16. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/schema.py +1 -1
  17. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/server.py +46 -4
  18. decision_toolkit-0.2.1/tests/test_contracts_conformance.py +109 -0
  19. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_fuzz_rest.py +7 -3
  20. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_operations.py +1 -1
  21. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_schema.py +1 -2
  22. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_server.py +89 -23
  23. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/uv.lock +1 -1
  24. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/.github/workflows/ci.yml +0 -0
  25. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/.github/workflows/docs.yml +0 -0
  26. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/.github/workflows/release.yml +0 -0
  27. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/.gitignore +0 -0
  28. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/CONTRIBUTING.md +0 -0
  29. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/LICENSE +0 -0
  30. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/SECURITY.md +0 -0
  31. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/THIRD_PARTY_NOTICES.md +0 -0
  32. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/README.md +0 -0
  33. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/__init__.py +0 -0
  34. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/load.py +0 -0
  35. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/results/2026-10-20-calibration.json +0 -0
  36. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/results/2026-10-20-load.json +0 -0
  37. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/results/2026-10-20-smoke.json +0 -0
  38. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/run.py +0 -0
  39. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/suites/calibration.jsonl +0 -0
  40. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/bench/suites/smoke.jsonl +0 -0
  41. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/BENCH.md +0 -0
  42. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/CLI.md +0 -0
  43. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/OPERATIONS.md +0 -0
  44. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/ROADMAP.md +0 -0
  45. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/models/README.md +0 -0
  46. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/models/gliner2.5-decide.md +0 -0
  47. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/models/gliner2.5-multi.md +0 -0
  48. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/models/julia-1.md +0 -0
  49. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/models/laya.md +0 -0
  50. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/docs/models/opendecider-nano.md +0 -0
  51. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/__main__.py +0 -0
  52. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/adapters/__init__.py +0 -0
  53. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/adapters/base.py +0 -0
  54. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/adapters/gliner2.py +0 -0
  55. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/adapters/gliner2_decide.py +0 -0
  56. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/adapters/julia.py +0 -0
  57. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/adapters/laya.py +0 -0
  58. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/adapters/opendecider.py +0 -0
  59. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/calibration.py +0 -0
  60. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/catalog.py +0 -0
  61. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/cli.py +0 -0
  62. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/config.py +0 -0
  63. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/contracts/common.py +0 -0
  64. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/decision_model.py +0 -0
  65. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/engine/__init__.py +0 -0
  66. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/errors.py +0 -0
  67. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/limits.py +0 -0
  68. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/metrics.py +0 -0
  69. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/models.py +0 -0
  70. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/observability.py +0 -0
  71. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/py.typed +0 -0
  72. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/registry.py +0 -0
  73. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/results.py +0 -0
  74. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/src/decision_toolkit/tracing.py +0 -0
  75. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/__init__.py +0 -0
  76. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/conftest.py +0 -0
  77. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_adapters_gliner2.py +0 -0
  78. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_adapters_gliner2_decide.py +0 -0
  79. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_adapters_julia.py +0 -0
  80. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_adapters_laya.py +0 -0
  81. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_adapters_opendecider.py +0 -0
  82. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_bench.py +0 -0
  83. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_catalog.py +0 -0
  84. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_cli.py +0 -0
  85. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_config.py +0 -0
  86. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_core_phase1.py +0 -0
  87. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_decision_model.py +0 -0
  88. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_integration_gliner2.py +0 -0
  89. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_models.py +0 -0
  90. {decision_toolkit-0.1.7 → decision_toolkit-0.2.1}/tests/test_results.py +0 -0
@@ -1,8 +1,38 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project are documented here. Version `0.1.7` is
3
+ All notable changes to this project are documented here. Version `0.2.1` is
4
4
  published on PyPI.
5
5
 
6
+ ## [0.2.1] — 2026-10-20
7
+
8
+ Metadata-only release: this project's documentation and project URLs now point
9
+ to `https://gia-uh.github.io/decision-toolkit/` instead of using the
10
+ `uhgia.org` custom domain owned by the `gia-uh/gia-uh.github.io` repository.
11
+ No runtime, model, CLI or REST behavior changed.
12
+
13
+ ## [0.2.0] — 2026-10-20
14
+
15
+ REST conformance correction. The previous documentation called the TypeSafe
16
+ contract "System One 1.14"; that version was not real. The public TypeSafe API
17
+ schema is `0.2.0`. This release aligns the request/response shapes and error
18
+ behavior with the published schemas, documents unsupported vision/assets as an
19
+ explicit text-only limitation, and removes invented OpenAI response fields.
20
+
21
+ ### Fixed
22
+
23
+ - System One is now pinned to the published TypeSafe API schema `0.2.0`, not
24
+ the invented `1.14` label. `/v1/info` reports `0.2.0`.
25
+ - System One accepts rich instructions/criteria, the standard `assets` field,
26
+ one-level scores and non-negative `output_tokens`; schema validation returns
27
+ `422` with a FastAPI-style `detail` array. Assets/images return `501` because
28
+ the catalog is text-only.
29
+ - OpenAI Decisions responses now expose the public `model`, `answers` and
30
+ `usage` fields (including usage details), without invented `id`, `object`,
31
+ `created_at`, `status` or `safety_identifier` fields. Toolkit metadata remains
32
+ namespaced under `x_decision_toolkit`.
33
+ - `variant` is explicitly documented as a local extension, not a TypeSafe or
34
+ OpenAI field. Standard clients select a model and omit it.
35
+
6
36
  ## [0.1.7] — 2026-10-20
7
37
 
8
38
  Variant consistency release. The selected quantization now agrees across the
@@ -128,7 +158,7 @@ README correction. No code or model behavior changed.
128
158
 
129
159
  ### Phase 2 — REST
130
160
 
131
- - Versioned System One 1.14 and OpenAI Decisions beta projections.
161
+ - Versioned System One and OpenAI Decisions beta projections.
132
162
  - Bearer auth, per-request model/variant, usage, standard error envelope.
133
163
  - OpenAI-shaped `/v1/models`, rich `/v1/decision-models`, readiness `503`.
134
164
  - Request ids, access logs, body limits, CORS and timeouts.
@@ -138,6 +168,8 @@ README correction. No code or model behavior changed.
138
168
  - Typed `DecisionModel` API with lifecycle, batching, async, usage, warnings,
139
169
  normalized confidence, capabilities, structured events and atomic model store.
140
170
 
171
+ [0.2.1]: https://pypi.org/project/decision-toolkit/0.2.1/
172
+ [0.2.0]: https://pypi.org/project/decision-toolkit/0.2.0/
141
173
  [0.1.7]: https://pypi.org/project/decision-toolkit/0.1.7/
142
174
  [0.1.6]: https://pypi.org/project/decision-toolkit/0.1.6/
143
175
  [0.1.5]: https://pypi.org/project/decision-toolkit/0.1.5/
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: decision-toolkit
3
- Version: 0.1.7
3
+ Version: 0.2.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
7
7
  Project-URL: Issues, https://github.com/gia-uh/decision-toolkit/issues
8
- Project-URL: Documentation, https://uhgia.org/decision-toolkit/
8
+ Project-URL: Documentation, https://gia-uh.github.io/decision-toolkit/
9
9
  Project-URL: Changelog, https://github.com/gia-uh/decision-toolkit/blob/main/CHANGELOG.md
10
10
  Author: Syalia S.R.L.
11
11
  Author-email: Yudivián Almeida Cruz <yudivian@gmail.com>
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
40
40
 
41
41
  [![PyPI](https://img.shields.io/pypi/v/decision-toolkit?style=flat-square)](https://pypi.org/project/decision-toolkit/)
42
42
  [![CI](https://github.com/gia-uh/decision-toolkit/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/gia-uh/decision-toolkit/actions/workflows/ci.yml)
43
- [![Docs](https://img.shields.io/badge/docs-online-blue?style=flat-square)](https://uhgia.org/decision-toolkit/)
43
+ [![Docs](https://img.shields.io/badge/docs-online-blue?style=flat-square)](https://gia-uh.github.io/decision-toolkit/)
44
44
 
45
45
  Local-first toolkit around **open-weights decision models**: typed **choice /
46
46
  yes-no (`noul`) / score** questions over a state (text or JSON), answered with a
@@ -52,14 +52,14 @@ contract** (how a model is fed and read). Three thin surfaces over the same API:
52
52
 
53
53
  Co-developed by [GIA-UH](https://github.com/gia-uh) and [Syalia S.R.L.](https://github.com/syalia-srl).
54
54
 
55
- **Documentation:** <https://uhgia.org/decision-toolkit/>. Start with
56
- [Examples](https://uhgia.org/decision-toolkit/EXAMPLES/) for the library, REST
55
+ **Documentation:** <https://gia-uh.github.io/decision-toolkit/>. Start with
56
+ [Examples](https://gia-uh.github.io/decision-toolkit/EXAMPLES/) for the library, REST
57
57
  and CLI. `dtk --help` and every subcommand help also include runnable examples.
58
58
 
59
59
  ## Installation
60
60
 
61
61
  ```bash
62
- uv pip install decision-toolkit # library + CLI (0.1.7 on PyPI)
62
+ uv pip install decision-toolkit # library + CLI (0.2.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.1.7/>. See
70
+ PyPI: <https://pypi.org/project/decision-toolkit/0.2.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
 
@@ -173,12 +173,16 @@ dtk serve --model gliner2.5-multi --warmup \
173
173
  --log-format json
174
174
  ```
175
175
 
176
- Endpoints: `POST /v1/systemone` (System One 1.14), `POST /v1/decisions`
176
+ Endpoints: `POST /v1/systemone` (TypeSafe System One), `POST /v1/decisions`
177
177
  (OpenAI Decisions beta), `GET /v1/models` (OpenAI shape),
178
178
  `GET /v1/decision-models` (rich catalog), `GET /v1/info`, `GET /health`,
179
- `GET /ready`. Requests may select `model` and `variant`; failures use the OpenAI
180
- `{"error": {...}}` envelope and every response carries `x-request-id`.
181
- `TYPESAFE_BASE_URL=http://localhost:8000` makes existing System One clients work.
179
+ `GET /ready`. Requests select `model`; `variant` is a documented local extension,
180
+ not part of either public API. System One request validation uses the TypeSafe
181
+ `422 {"detail": [...]}` shape; OpenAI Decisions uses its
182
+ `{"error": {...}}` envelope. Every response carries `x-request-id`.
183
+ A standard System One client that sends `model`, `state` and `questions` can
184
+ point at `http://localhost:8000`; image/asset and `box` requests are outside the
185
+ current text-only subset.
182
186
  See [docs/REST.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/REST.md) for the pinned contracts, limits, auth and
183
187
  the standard-evolution process.
184
188
 
@@ -250,7 +254,7 @@ another one.
250
254
  A variant becomes default only after passing the parity gate (top-1 ≥ 99%,
251
255
  mean |Δp| ≤ 0.02, accuracy delta ≤ 1 pp against the fp32 build on a fixed
252
256
  sample) and measuring latency on CPU. Unverified conversions are not catalogued
253
- and never become a silent fallback. See [Variants and quantization](https://uhgia.org/decision-toolkit/VARIANTS/)
257
+ and never become a silent fallback. See [Variants and quantization](https://gia-uh.github.io/decision-toolkit/VARIANTS/)
254
258
  for the complete table, cache behavior and commands.
255
259
 
256
260
  ## Operations
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![PyPI](https://img.shields.io/pypi/v/decision-toolkit?style=flat-square)](https://pypi.org/project/decision-toolkit/)
4
4
  [![CI](https://github.com/gia-uh/decision-toolkit/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/gia-uh/decision-toolkit/actions/workflows/ci.yml)
5
- [![Docs](https://img.shields.io/badge/docs-online-blue?style=flat-square)](https://uhgia.org/decision-toolkit/)
5
+ [![Docs](https://img.shields.io/badge/docs-online-blue?style=flat-square)](https://gia-uh.github.io/decision-toolkit/)
6
6
 
7
7
  Local-first toolkit around **open-weights decision models**: typed **choice /
8
8
  yes-no (`noul`) / score** questions over a state (text or JSON), answered with a
@@ -14,14 +14,14 @@ contract** (how a model is fed and read). Three thin surfaces over the same API:
14
14
 
15
15
  Co-developed by [GIA-UH](https://github.com/gia-uh) and [Syalia S.R.L.](https://github.com/syalia-srl).
16
16
 
17
- **Documentation:** <https://uhgia.org/decision-toolkit/>. Start with
18
- [Examples](https://uhgia.org/decision-toolkit/EXAMPLES/) for the library, REST
17
+ **Documentation:** <https://gia-uh.github.io/decision-toolkit/>. Start with
18
+ [Examples](https://gia-uh.github.io/decision-toolkit/EXAMPLES/) for the library, REST
19
19
  and CLI. `dtk --help` and every subcommand help also include runnable examples.
20
20
 
21
21
  ## Installation
22
22
 
23
23
  ```bash
24
- uv pip install decision-toolkit # library + CLI (0.1.7 on PyPI)
24
+ uv pip install decision-toolkit # library + CLI (0.2.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.1.7/>. See
32
+ PyPI: <https://pypi.org/project/decision-toolkit/0.2.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
 
@@ -135,12 +135,16 @@ dtk serve --model gliner2.5-multi --warmup \
135
135
  --log-format json
136
136
  ```
137
137
 
138
- Endpoints: `POST /v1/systemone` (System One 1.14), `POST /v1/decisions`
138
+ Endpoints: `POST /v1/systemone` (TypeSafe System One), `POST /v1/decisions`
139
139
  (OpenAI Decisions beta), `GET /v1/models` (OpenAI shape),
140
140
  `GET /v1/decision-models` (rich catalog), `GET /v1/info`, `GET /health`,
141
- `GET /ready`. Requests may select `model` and `variant`; failures use the OpenAI
142
- `{"error": {...}}` envelope and every response carries `x-request-id`.
143
- `TYPESAFE_BASE_URL=http://localhost:8000` makes existing System One clients work.
141
+ `GET /ready`. Requests select `model`; `variant` is a documented local extension,
142
+ not part of either public API. System One request validation uses the TypeSafe
143
+ `422 {"detail": [...]}` shape; OpenAI Decisions uses its
144
+ `{"error": {...}}` envelope. Every response carries `x-request-id`.
145
+ A standard System One client that sends `model`, `state` and `questions` can
146
+ point at `http://localhost:8000`; image/asset and `box` requests are outside the
147
+ current text-only subset.
144
148
  See [docs/REST.md](https://github.com/gia-uh/decision-toolkit/blob/main/docs/REST.md) for the pinned contracts, limits, auth and
145
149
  the standard-evolution process.
146
150
 
@@ -212,7 +216,7 @@ another one.
212
216
  A variant becomes default only after passing the parity gate (top-1 ≥ 99%,
213
217
  mean |Δp| ≤ 0.02, accuracy delta ≤ 1 pp against the fp32 build on a fixed
214
218
  sample) and measuring latency on CPU. Unverified conversions are not catalogued
215
- and never become a silent fallback. See [Variants and quantization](https://uhgia.org/decision-toolkit/VARIANTS/)
219
+ and never become a silent fallback. See [Variants and quantization](https://gia-uh.github.io/decision-toolkit/VARIANTS/)
216
220
  for the complete table, cache behavior and commands.
217
221
 
218
222
  ## Operations
@@ -1,7 +1,7 @@
1
1
  # Core programmatic API (phase 1)
2
2
 
3
3
  The programmatic API is the **superset**: the most expressive layer of the
4
- toolkit. The REST server is a standards-compliant projection of a subset of
4
+ toolkit. The REST server is a documented, conformance-tested projection of a subset of
5
5
  this API, and the CLI is our own operational subset. Nothing in this document
6
6
  belongs to a wire contract.
7
7
 
@@ -132,6 +132,7 @@ dtk serve --model gliner2.5-multi --warmup --port 8000
132
132
  curl -s http://127.0.0.1:8000/v1/systemone \
133
133
  -H "Content-Type: application/json" \
134
134
  -d '{
135
+ "model": "gliner2.5-multi",
135
136
  "state": "Meeting with Acme: 10% discount, sign next week.",
136
137
  "questions": {
137
138
  "cat": {
@@ -150,6 +151,7 @@ curl -s http://127.0.0.1:8000/v1/systemone \
150
151
  curl -s http://127.0.0.1:8000/v1/decisions \
151
152
  -H "Content-Type: application/json" \
152
153
  -d '{
154
+ "model": "gliner2.5-multi",
153
155
  "input": "Please change my seat to an aisle.",
154
156
  "questions": [{
155
157
  "type": "choice",
@@ -163,6 +165,8 @@ curl -s http://127.0.0.1:8000/v1/decisions \
163
165
  }' | jq
164
166
  ```
165
167
 
168
+ `variant` is a decision-toolkit extension; omit it for a standard client.
169
+
166
170
  ### Model per request and auth
167
171
 
168
172
  ```bash
@@ -7,7 +7,9 @@ file was written, so the name was available). The first release was `0.1.0`;
7
7
  package management and builds to uv but was published to TestPyPI only; `0.1.5`
8
8
  fixed TestPyPI verification for uv; `0.1.6` added complete variant visibility
9
9
  and selection in the CLI and documentation; `0.1.7` aligned variant selection
10
- across the library, CLI and REST API.
10
+ across the library, CLI and REST API; `0.2.0` corrected the REST conformance
11
+ claims and wire shapes to the published TypeSafe and OpenAI schemas; `0.2.1`
12
+ changed the public documentation URLs to this repository's Pages site.
11
13
 
12
14
  ## Local build and verification
13
15
 
@@ -34,7 +36,7 @@ Clean install test (no `PYTHONPATH`):
34
36
  ```bash
35
37
  uv venv --python 3.12 /tmp/dtk-wheel
36
38
  uv pip install --python /tmp/dtk-wheel/bin/python \
37
- dist/decision_toolkit-0.1.7-py3-none-any.whl
39
+ dist/decision_toolkit-0.2.1-py3-none-any.whl
38
40
  uv pip check --python /tmp/dtk-wheel/bin/python
39
41
  /tmp/dtk-wheel/bin/dtk --version
40
42
  /tmp/dtk-wheel/bin/dtk models list --json
@@ -45,7 +47,7 @@ With inference and server extras:
45
47
 
46
48
  ```bash
47
49
  uv pip install --python /tmp/dtk-wheel/bin/python \
48
- "dist/decision_toolkit-0.1.7-py3-none-any.whl[onnx,server,tracing]"
50
+ "dist/decision_toolkit-0.2.1-py3-none-any.whl[onnx,server,tracing]"
49
51
  /tmp/dtk-wheel/bin/dtk decide --model gliner2.5-decide ...
50
52
  /tmp/dtk-wheel/bin/dtk serve --model gliner2.5-decide --warmup --port 8016
51
53
  curl -s http://127.0.0.1:8016/health
@@ -79,8 +81,8 @@ No long-lived PyPI token is needed; the workflow uses GitHub OIDC
79
81
  4. Create and push an annotated tag:
80
82
 
81
83
  ```bash
82
- git tag -a v0.1.7 -m "decision-toolkit 0.1.7"
83
- git push origin v0.1.7
84
+ git tag -a v0.2.1 -m "decision-toolkit 0.2.1"
85
+ git push origin v0.2.1
84
86
  ```
85
87
 
86
88
  5. `.github/workflows/release.yml` builds once, publishes to **TestPyPI**, then
@@ -129,9 +131,9 @@ to TestPyPI without publishing to PyPI or creating a GitHub release.
129
131
 
130
132
  ## Published release
131
133
 
132
- `0.1.7` is published through Trusted Publishing on 2026-10-20:
134
+ `0.2.1` is published through Trusted Publishing on 2026-10-20:
133
135
 
134
- <https://pypi.org/project/decision-toolkit/0.1.7/>
136
+ <https://pypi.org/project/decision-toolkit/0.2.1/>
135
137
 
136
138
  `0.1.0` was the first release; `0.1.1` refreshed the PyPI description; `0.1.2`
137
139
  validated the OIDC TestPyPI → PyPI flow; `0.1.3` added post-publish install
@@ -139,7 +141,8 @@ verification and the GitHub Pages documentation site; `0.1.4` moved the
139
141
  development, build, CI, release and documentation environments to uv but was
140
142
  published to TestPyPI only; `0.1.5` fixed TestPyPI resolution; `0.1.6` added
141
143
  complete variant/quantization visibility and CLI selection; `0.1.7` aligned
142
- selection across library, CLI and REST.
144
+ selection across library, CLI and REST; `0.2.0` corrected the REST conformance
145
+ claims and wire shapes; `0.2.1` changed the public documentation URLs.
143
146
 
144
147
  The tag-triggered workflow remains the preferred path for future releases once
145
148
  the TestPyPI/PyPI trusted publishers and protected environments are configured.
@@ -1,37 +1,42 @@
1
1
  # REST projection (phase 2)
2
2
 
3
- REST is a **standards-compliant projection** of the programmatic core, not a
4
- second implementation. The core remains the superset; the server selects a
5
- subset, maps it to a pinned wire contract, and adds transport concerns (auth,
6
- request ids, limits, CORS, logging).
3
+ REST is a **documented projection** of the programmatic core, not a second
4
+ implementation. The core remains the superset; the server selects a supported
5
+ subset, maps it to the published wire contracts, and adds transport concerns
6
+ (auth, request ids, limits, CORS, logging).
7
7
 
8
8
  ```
9
9
  Programmatic core (superset)
10
- │ pure projection + conformance
10
+ │ pure projection + conformance tests
11
11
  ▼
12
- REST (subset; System One 1.14 + OpenAI Decisions beta)
12
+ REST (supported subset; TypeSafe System One + OpenAI Decisions beta)
13
13
  ```
14
14
 
15
+ Compatibility is tested against the published shapes, not against a local
16
+ invention. The current models are text-only, so image/assets and `box` requests
17
+ are explicitly rejected rather than silently approximated.
18
+
15
19
  The wire models live in `decision_toolkit/contracts/`, the multi-model registry
16
20
  in `decision_toolkit/registry.py`, and `server.py` contains only transport and
17
21
  FastAPI glue.
18
22
 
19
23
  ## Pinned contracts
20
24
 
21
- | Contract | Version | Module |
22
- | --- | --- | --- |
23
- | System One (TypeSafe-compatible) | `1.14` | `contracts/systemone_v1.py` |
24
- | OpenAI Decisions | `beta` | `contracts/openai_decisions_beta.py` |
25
+ | Contract | Version | Reference | Module |
26
+ | --- | --- | --- | --- |
27
+ | TypeSafe System One | API schema `0.2.0` | <https://docs.typesafe.ai/api> | `contracts/systemone_v1.py` |
28
+ | OpenAI Decisions | `beta` | <https://developers.openai.com/api/docs/guides/decisions> | `contracts/openai_decisions_beta.py` |
25
29
 
26
- `GET /v1/info` exposes both versions. The reference for System One is the
27
- TypeSafe 1.14 API as implemented by llama.cpp's `POST /v1/systemone`; the
28
- OpenAI dialect is the beta Decisions shape already translated by the toolkit.
30
+ `GET /v1/info` exposes both versions. "System One 1.14" was an incorrect label
31
+ in earlier documentation; the published TypeSafe API schema is `0.2.0`. The
32
+ implementation targets that schema and the published OpenAI beta shape, with the
33
+ limitations listed below.
29
34
 
30
35
  ## Endpoints
31
36
 
32
37
  | Method | Path | Purpose |
33
38
  | --- | --- | --- |
34
- | `POST` | `/v1/systemone` | System One 1.14 decision. |
39
+ | `POST` | `/v1/systemone` | TypeSafe System One decision. |
35
40
  | `POST` | `/v1/decisions` | OpenAI Decisions beta decision. |
36
41
  | `GET` | `/v1/models` | OpenAI model-list shape. |
37
42
  | `GET` | `/v1/models/{id}` | One model in the OpenAI shape. |
@@ -44,28 +49,33 @@ OpenAI dialect is the beta Decisions shape already translated by the toolkit.
44
49
  Toolkit-only metadata is under `x_decision_toolkit`, never mixed into standard
45
50
  fields. `/v1/decision-models` is the rich, non-standard catalog endpoint.
46
51
 
47
- ## Variants
52
+ ## Variants (local extension)
48
53
 
49
- Both decision endpoints accept an optional `variant` alongside `model`:
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**:
50
56
 
51
- - Request `variant` wins.
57
+ - Request `variant` wins when a local client sends it.
52
58
  - Otherwise, a server started with `dtk serve --variant` uses that default.
53
59
  - Otherwise, the variant selected with `dtk models use` is used when the server
54
60
  is serving the current model.
55
61
  - Otherwise, the catalog default (the `*` variant in `dtk models list`) is used.
56
62
 
57
- The selected variant is echoed in `x_decision_toolkit.variant`.
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.
58
67
  `/v1/models`, `/v1/models/{id}`, `/v1/decision-models`, `/v1/info` and `/ready`
59
68
  expose the configured catalog variants, the default, cached variants, and the
60
69
  current selection. See [Variants and quantization](VARIANTS.md) for the catalog
61
70
  and download/select commands.
62
71
 
63
- ## System One 1.14
72
+ ## System One (TypeSafe API schema 0.2.0)
64
73
 
65
- Request:
74
+ Request (standard fields, plus the optional local `model` routing extension):
66
75
 
67
76
  ```json
68
77
  {
78
+ "model": "gliner2.5-multi",
69
79
  "state": "I was charged twice and nobody replied.",
70
80
  "questions": {
71
81
  "route": {
@@ -104,9 +114,9 @@ Response:
104
114
  "confidence": 0.4
105
115
  }
106
116
  },
107
- "usage": {"input_tokens": 239, "output_tokens": 0},
117
+ "usage": {"input_tokens": 239, "output_tokens": 12},
108
118
  "x_decision_toolkit": {
109
- "spec": "1.14",
119
+ "spec": "0.2.0",
110
120
  "request_id": "req-123",
111
121
  "variant": "fp16",
112
122
  "adapter": "gliner2",
@@ -121,16 +131,23 @@ Response:
121
131
 
122
132
  Standard fields:
123
133
 
124
- - `usage.output_tokens` is **always 0** (decisions generate no text).
134
+ - `usage.output_tokens` is a non-negative count. The current core reports the
135
+ tokenizer count of the serialized answers (it may be non-zero); do not assume
136
+ it is always zero.
125
137
  - `usage.input_tokens` comes from the core token accounting.
126
138
  - The full core usage, including the explicit `estimated` flag, is under
127
139
  `x_decision_toolkit.usage`.
128
- - `choice` requires a non-empty criteria object; `score` needs 2–10 levels;
129
- `noul` may omit criteria. Invalid bodies return `400`.
130
- - `files`/`images` accept data URLs only. Text-only models return `501` with
131
- `code: "multimodal_not_supported"`; malformed/non-data URLs return `400`.
132
- - `model` and `variant` are optional routing extensions for a multi-model local
133
- server. An unqualified request uses the configured default model.
140
+ - `choice` requires a non-empty criteria object; System One `score` accepts
141
+ `1`–`10` ordered levels; `noul` may omit criteria.
142
+ - Request-schema and question-validation failures return `422` with the
143
+ FastAPI-style `{"detail": [...]}` body used by the TypeSafe schema. Model
144
+ lookup, capability and transport failures keep their HTTP status and the
145
+ documented error envelope.
146
+ - Standard `assets` are accepted by the schema but return `501` because the
147
+ current catalog is text-only. Legacy `files`/`images` fields are local
148
+ extensions; valid data URLs also return `501`, malformed values return `422`.
149
+ - `model` is optional only as a local convenience; standard clients send it.
150
+ `variant` is a local extension, not part of the TypeSafe schema.
134
151
 
135
152
  ## OpenAI Decisions beta
136
153
 
@@ -146,9 +163,7 @@ Request:
146
163
  {"type": "score", "name": "urgency", "instructions": "How urgent?",
147
164
  "levels": [{"label": "low"}, {"label": "high"}]}
148
165
  ],
149
- "safety_identifier": "user-hash",
150
- "model": "gliner2.5-multi",
151
- "variant": "fp16"
166
+ "model": "gliner2.5-multi"
152
167
  }
153
168
  ```
154
169
 
@@ -156,10 +171,6 @@ Response:
156
171
 
157
172
  ```json
158
173
  {
159
- "id": "req-123",
160
- "object": "response",
161
- "created_at": 1790000000,
162
- "status": "completed",
163
174
  "model": "gliner2.5-multi",
164
175
  "answers": [
165
176
  {"type": "predicate", "name": "refund", "probability": 0.93},
@@ -172,8 +183,13 @@ Response:
172
183
  {"value": 1, "label": "high", "probability": 0.8}],
173
184
  "confidence": 0.8}
174
185
  ],
175
- "usage": {"input_tokens": 92, "output_tokens": 0, "total_tokens": 92},
176
- "safety_identifier": "user-hash",
186
+ "usage": {
187
+ "input_tokens": 92,
188
+ "output_tokens": 12,
189
+ "total_tokens": 104,
190
+ "input_tokens_details": {"cached_tokens": 0, "cache_write_tokens": 0},
191
+ "output_tokens_details": {"reasoning_tokens": 0}
192
+ },
177
193
  "x_decision_toolkit": {
178
194
  "spec": "beta",
179
195
  "request_id": "req-123",
@@ -182,15 +198,20 @@ Response:
182
198
  "latency_ms": 350.1,
183
199
  "truncated": false,
184
200
  "warnings": [],
185
- "usage": {"input_tokens": 92, "output_tokens": 10, "total_tokens": 102, "estimated": true}
201
+ "usage": {"input_tokens": 92, "output_tokens": 12, "total_tokens": 104, "estimated": true}
186
202
  }
187
203
  }
188
204
  ```
189
205
 
190
- - Original choice values (numbers, booleans, strings) are echoed back, not the
191
- internal System One labels.
206
+ - The public beta response fields are `model`, `answers` and `usage`; there is
207
+ no invented `id`, `object`, `created_at`, `status` or `safety_identifier`.
208
+ - Original choice values are echoed back, not the internal System One labels.
209
+ The published beta accepts string/boolean values; the toolkit also accepts
210
+ numbers as a local extension.
192
211
  - Duplicate question names return `400`.
193
- - Images in `input` return `501` (`multimodal_not_supported`).
212
+ - Images in `input` return `501` (`multimodal_not_supported`); the published
213
+ beta supports inline images, but the current catalog has no vision model.
214
+ - `variant` is a local extension; standard clients omit it.
194
215
  - OpenAI SDK clients can list models (`client.models.list()`) and parse the
195
216
  error envelope (`{"error": {message, type, param, code}}`).
196
217
 
@@ -222,9 +243,11 @@ A model is not "ready" merely because it is configured:
222
243
 
223
244
  ## Limits and transport
224
245
 
225
- - 1–128 questions.
226
- - 1–255 choice options; 2–10 score levels.
227
- - 8 files/images maximum (the current catalog supports 0).
246
+ - 1–128 questions (local limit; the TypeSafe schema has no maximum).
247
+ - 1–255 choice options; 1–10 System One score levels; OpenAI score levels
248
+ require at least 2.
249
+ - 8 legacy files/images maximum (the current catalog supports 0); standard
250
+ `assets` and images are rejected with `501`.
228
251
  - Request body limit (`--max-body-mb`, default 16) returns `413`.
229
252
  - Per-decision timeout (`--timeout`, default 120 s) returns `504`.
230
253
  - CORS origins are explicit (`--cors-origin`, repeatable).
@@ -235,22 +258,22 @@ A model is not "ready" merely because it is configured:
235
258
 
236
259
  ## Error mapping
237
260
 
238
- All errors use `{"error": {"message", "type", "param", "code"}}`.
261
+ `/v1/systemone` request-validation failures follow the TypeSafe schema:
262
+ `422` with `{"detail": [...]}`. `/v1/decisions` validation and toolkit domain
263
+ errors use the OpenAI envelope
264
+ `{"error": {"message", "type", "param", "code"}}`.
239
265
 
240
- | Condition | HTTP | code |
266
+ | Condition | System One | OpenAI Decisions |
241
267
  | --- | --- | --- |
242
- | Invalid question/body | `400` | `invalid_question` / `invalid_request` |
243
- | Unsupported capability (`box`, assets) | `400` | `unsupported_capability` |
244
- | Unknown model | `404` | `model_not_found` |
245
- | Model not downloaded | `503` | `model_not_downloaded` |
246
- | Adapter unavailable | `503` | `adapter_not_available` |
247
- | Adapter not implemented | `501` | `adapter_not_implemented` |
248
- | Images/audio unsupported | `501` | `multimodal_not_supported` |
249
- | Truncation in strict mode | `400` | `truncated_input` |
250
- | Body too large | `413` | `request_too_large` |
251
- | Missing/invalid bearer | `401` | `authentication_error` |
252
- | Timeout | `504` | `timeout` |
253
- | Backend failure | `500` | `backend_failure` / `adapter_error` |
268
+ | Request-schema validation | `422` + `detail` | `400` + error envelope |
269
+ | Toolkit question validation | `422` + `detail` | `400` + error envelope |
270
+ | Unknown model | `404` | `404` |
271
+ | Model not downloaded | `503` | `503` |
272
+ | Unsupported `box` / assets / images | `501` | `501` |
273
+ | Truncation in strict mode | `400` | `400` |
274
+ | Body too large | `413` | `413` |
275
+ | Missing/invalid bearer | `401` | `401` |
276
+ | Timeout | `504` | `504` |
254
277
 
255
278
  The CLI/REST never parse error strings to decide behavior; they use the stable
256
279
  `code`.
@@ -277,16 +300,17 @@ Adding a new spec version means adding a new module, not editing the old one.
277
300
 
278
301
  ## Compatibility matrix (current)
279
302
 
280
- | Feature | System One 1.14 | OpenAI Decisions beta |
303
+ | Feature | System One (TypeSafe 0.2.0) | OpenAI Decisions beta |
281
304
  | --- | --- | --- |
282
305
  | `choice` / `noul` / `score` | yes | `choice` / `predicate` / `score` |
283
306
  | `box` | typed by core; rejected by current text models | no |
284
307
  | Text state | yes | `input` string |
285
308
  | JSON state | yes | message/string `input` |
286
- | Images | `501` for current models | `501` for current models |
287
- | Usage | `input_tokens` + `output_tokens=0` | input/output/total |
288
- | Per-request model/variant | extension | `model` + `variant` extension |
289
- | Errors | OpenAI-style envelope | OpenAI envelope |
309
+ | Images/assets | `501` for current models | `501` for current models |
310
+ | Usage | `input_tokens` + non-negative `output_tokens` | input/output/total + details |
311
+ | Per-request model | standard field; optional local default | standard field; optional local default |
312
+ | `variant` | local extension | local extension |
313
+ | Validation errors | `422` + `detail` | `400` + envelope |
290
314
  | Auth / CORS / limits | server-level | server-level |
291
315
 
292
316
  ## Running
@@ -303,8 +327,8 @@ at `http://localhost:8000/v1`.
303
327
 
304
328
  ## Testing
305
329
 
306
- - System One contract tests cover the exact request/response fields, usage
307
- `output_tokens=0`, invalid request matrix and image status codes.
330
+ - System One contract tests cover the published request/response fields,
331
+ non-negative usage, the invalid request matrix and image/asset status codes.
308
332
  - OpenAI contract tests use the official `openai` Python SDK for
309
333
  `models.list()` and error-envelope parsing.
310
334
  - Equivalence tests send the same core decision through HTTP and compare
@@ -46,13 +46,15 @@ precedence is:
46
46
  | --- | --- | --- |
47
47
  | Library | `DecisionModel("gliner2.5-multi", variant="fp32")` | persisted variant for the current model, else catalog default |
48
48
  | CLI | `--variant fp32` | variant selected by `dtk models use`, else catalog default |
49
- | REST | `"variant": "fp32"` in the request body | server `--variant`, else persisted variant for the current model, else catalog default |
49
+ | REST (local extension) | `"variant": "fp32"` in the request body | server `--variant`, else persisted variant for the current model, else catalog default |
50
50
 
51
51
  `DecisionModel.variant` and the CLI report the selected variant (`id@variant` in
52
- `dtk models current`). REST responses expose it under
53
- `x_decision_toolkit.variant`; `/v1/decision-models` includes `current` and
54
- `current_variant`, plus all `variants` and `cached_variants`. A server started
55
- with `--model`/`--variant` reports that default in `/v1/info` and `/ready`.
52
+ `dtk models current`). REST responses expose it under the namespaced
53
+ `x_decision_toolkit.variant` extension; `/v1/decision-models` includes
54
+ `current` and `current_variant`, plus all `variants` and `cached_variants`.
55
+ `variant` is **not** part of TypeSafe or OpenAI; standard clients should omit it
56
+ and rely on the server's `--variant`/persisted default. A server started with
57
+ `--model`/`--variant` reports that default in `/v1/info` and `/ready`.
56
58
 
57
59
  The variant must have been downloaded before a library call or REST request
58
60
  loads it. An unknown variant returns a model-not-found error; a known but
@@ -18,7 +18,7 @@ PyPI: <https://pypi.org/project/decision-toolkit/>
18
18
 
19
19
  - [Examples](EXAMPLES.md) — library, REST and CLI.
20
20
  - [Core API](CORE.md) — the superset used by every surface.
21
- - [REST](REST.md) — System One 1.14 + OpenAI Decisions beta.
21
+ - [REST](REST.md) — TypeSafe System One + OpenAI Decisions beta.
22
22
  - [CLI](CLI.md) — operational commands and batch workflows.
23
23
  - [Variants and quantization](VARIANTS.md) — every catalogued build and how to download/select it.
24
24
  - [Operations](OPERATIONS.md) — rate limits, metrics, tracing and runbook.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "decision-toolkit"
7
- version = "0.1.7"
7
+ version = "0.2.1"
8
8
  description = "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."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -73,7 +73,7 @@ docs = [
73
73
  Homepage = "https://github.com/gia-uh/decision-toolkit"
74
74
  Repository = "https://github.com/gia-uh/decision-toolkit"
75
75
  Issues = "https://github.com/gia-uh/decision-toolkit/issues"
76
- Documentation = "https://uhgia.org/decision-toolkit/"
76
+ Documentation = "https://gia-uh.github.io/decision-toolkit/"
77
77
  Changelog = "https://github.com/gia-uh/decision-toolkit/blob/main/CHANGELOG.md"
78
78
 
79
79
  [project.scripts]