noulxp 0.4.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.
Files changed (141) hide show
  1. noulxp-0.4.0/.gitignore +14 -0
  2. noulxp-0.4.0/CHANGELOG.md +165 -0
  3. noulxp-0.4.0/CONTRIBUTING.md +50 -0
  4. noulxp-0.4.0/LICENSE +176 -0
  5. noulxp-0.4.0/NOTICE +21 -0
  6. noulxp-0.4.0/PAPER.md +74 -0
  7. noulxp-0.4.0/PKG-INFO +353 -0
  8. noulxp-0.4.0/README.md +303 -0
  9. noulxp-0.4.0/RELEASING.md +72 -0
  10. noulxp-0.4.0/SPEC.md +839 -0
  11. noulxp-0.4.0/VALIDATION.md +237 -0
  12. noulxp-0.4.0/badge/BADGE.md +51 -0
  13. noulxp-0.4.0/badge/noulxp-compatible.svg +12 -0
  14. noulxp-0.4.0/badge/noulxp-portable.svg +12 -0
  15. noulxp-0.4.0/legacy/opendxp/README.md +16 -0
  16. noulxp-0.4.0/legacy/opendxp/pyproject.toml +40 -0
  17. noulxp-0.4.0/legacy/opendxp/src/opendxp/__init__.py +49 -0
  18. noulxp-0.4.0/legacy/opendxp/src/opendxp/__main__.py +5 -0
  19. noulxp-0.4.0/legacy/opendxp/src/opendxp_cli.py +12 -0
  20. noulxp-0.4.0/pyproject.toml +87 -0
  21. noulxp-0.4.0/schemas/calibration.schema.json +34 -0
  22. noulxp-0.4.0/schemas/check-report.schema.json +41 -0
  23. noulxp-0.4.0/schemas/conformance-case.schema.json +42 -0
  24. noulxp-0.4.0/schemas/error.schema.json +29 -0
  25. noulxp-0.4.0/schemas/models.schema.json +51 -0
  26. noulxp-0.4.0/schemas/noulxp.schema.json +101 -0
  27. noulxp-0.4.0/schemas/prompt.schema.json +191 -0
  28. noulxp-0.4.0/schemas/request.schema.json +62 -0
  29. noulxp-0.4.0/schemas/response.schema.json +56 -0
  30. noulxp-0.4.0/schemas/template.schema.json +98 -0
  31. noulxp-0.4.0/scripts/anyjev_token_ids.py +90 -0
  32. noulxp-0.4.0/scripts/build_requests.py +862 -0
  33. noulxp-0.4.0/scripts/decider_token_ids.py +59 -0
  34. noulxp-0.4.0/scripts/julia_parity.py +71 -0
  35. noulxp-0.4.0/scripts/llama_numerics.py +105 -0
  36. noulxp-0.4.0/scripts/prefix_sharing.py +152 -0
  37. noulxp-0.4.0/src/noulxp/__init__.py +26 -0
  38. noulxp-0.4.0/src/noulxp/__main__.py +3 -0
  39. noulxp-0.4.0/src/noulxp/answers.py +96 -0
  40. noulxp-0.4.0/src/noulxp/bench.py +441 -0
  41. noulxp-0.4.0/src/noulxp/calibrate.py +429 -0
  42. noulxp-0.4.0/src/noulxp/calibration.py +112 -0
  43. noulxp-0.4.0/src/noulxp/cli.py +543 -0
  44. noulxp-0.4.0/src/noulxp/conformance.py +349 -0
  45. noulxp-0.4.0/src/noulxp/data/requests-0.1.jsonl +52 -0
  46. noulxp-0.4.0/src/noulxp/errors.py +23 -0
  47. noulxp-0.4.0/src/noulxp/export/__init__.py +7 -0
  48. noulxp-0.4.0/src/noulxp/export/anyjev.py +279 -0
  49. noulxp-0.4.0/src/noulxp/export/common.py +118 -0
  50. noulxp-0.4.0/src/noulxp/export/decider.py +169 -0
  51. noulxp-0.4.0/src/noulxp/export/encoder.py +105 -0
  52. noulxp-0.4.0/src/noulxp/export/julia.py +383 -0
  53. noulxp-0.4.0/src/noulxp/export/laya.py +251 -0
  54. noulxp-0.4.0/src/noulxp/export/onnx_graph.py +249 -0
  55. noulxp-0.4.0/src/noulxp/mcp.py +257 -0
  56. noulxp-0.4.0/src/noulxp/native/__init__.py +145 -0
  57. noulxp-0.4.0/src/noulxp/native/anyjev.py +120 -0
  58. noulxp-0.4.0/src/noulxp/native/decider.py +348 -0
  59. noulxp-0.4.0/src/noulxp/native/julia.py +317 -0
  60. noulxp-0.4.0/src/noulxp/package.py +159 -0
  61. noulxp-0.4.0/src/noulxp/profiles/__init__.py +0 -0
  62. noulxp-0.4.0/src/noulxp/profiles/causal_letters.py +632 -0
  63. noulxp-0.4.0/src/noulxp/profiles/encoder_markers.py +516 -0
  64. noulxp-0.4.0/src/noulxp/profiles/typed.py +298 -0
  65. noulxp-0.4.0/src/noulxp/providers.py +190 -0
  66. noulxp-0.4.0/src/noulxp/request.py +114 -0
  67. noulxp-0.4.0/src/noulxp/rotations.py +77 -0
  68. noulxp-0.4.0/src/noulxp/runtime.py +45 -0
  69. noulxp-0.4.0/src/noulxp/schemas.py +50 -0
  70. noulxp-0.4.0/src/noulxp/server.py +205 -0
  71. noulxp-0.4.0/src/noulxp/serving.py +291 -0
  72. noulxp-0.4.0/src/noulxp/spec.py +89 -0
  73. noulxp-0.4.0/src/noulxp/text.py +103 -0
  74. noulxp-0.4.0/src/noulxp/tokens.py +39 -0
  75. noulxp-0.4.0/src/noulxp/validate.py +73 -0
  76. noulxp-0.4.0/tests/conftest.py +46 -0
  77. noulxp-0.4.0/tests/generators.py +70 -0
  78. noulxp-0.4.0/tests/oracles.py +299 -0
  79. noulxp-0.4.0/tests/test_answers.py +65 -0
  80. noulxp-0.4.0/tests/test_attention.py +83 -0
  81. noulxp-0.4.0/tests/test_bench.py +123 -0
  82. noulxp-0.4.0/tests/test_calibrate.py +294 -0
  83. noulxp-0.4.0/tests/test_calibration.py +69 -0
  84. noulxp-0.4.0/tests/test_conformance.py +346 -0
  85. noulxp-0.4.0/tests/test_mcp.py +206 -0
  86. noulxp-0.4.0/tests/test_official_mapping.py +96 -0
  87. noulxp-0.4.0/tests/test_prompt.py +133 -0
  88. noulxp-0.4.0/tests/test_providers.py +71 -0
  89. noulxp-0.4.0/tests/test_request.py +57 -0
  90. noulxp-0.4.0/tests/test_schemas.py +115 -0
  91. noulxp-0.4.0/tests/test_serving.py +347 -0
  92. noulxp-0.4.0/tests/test_spec.py +13 -0
  93. noulxp-0.4.0/tests/test_template.py +134 -0
  94. noulxp-0.4.0/tests/test_text.py +25 -0
  95. noulxp-0.4.0/tests/test_typed.py +528 -0
  96. noulxp-0.4.0/validation/anyjev-qwen3-1.7b/calibration.json +11 -0
  97. noulxp-0.4.0/validation/anyjev-qwen3-1.7b/check-cpu-default-decode.json +128 -0
  98. noulxp-0.4.0/validation/anyjev-qwen3-1.7b/check-cpu-q8_0.json +352 -0
  99. noulxp-0.4.0/validation/anyjev-qwen3-1.7b/check-cpu.json +113 -0
  100. noulxp-0.4.0/validation/anyjev-qwen3-1.7b/conformance.jsonl +52 -0
  101. noulxp-0.4.0/validation/anyjev-qwen3-1.7b/odxp.json +76 -0
  102. noulxp-0.4.0/validation/anyjev-qwen3-1.7b/prompt.json +158 -0
  103. noulxp-0.4.0/validation/anyjev-qwen3-1.7b/token-ids.json +6 -0
  104. noulxp-0.4.0/validation/decider-2b/calibration.json +18 -0
  105. noulxp-0.4.0/validation/decider-2b/check-cpu.json +113 -0
  106. noulxp-0.4.0/validation/decider-2b/check-metal.json +240 -0
  107. noulxp-0.4.0/validation/decider-2b/conformance.jsonl +52 -0
  108. noulxp-0.4.0/validation/decider-2b/llama-numerics.json +20 -0
  109. noulxp-0.4.0/validation/decider-2b/odxp.json +67 -0
  110. noulxp-0.4.0/validation/decider-2b/prefix-sharing-cpu.json +18 -0
  111. noulxp-0.4.0/validation/decider-2b/prefix-sharing-metal.json +18 -0
  112. noulxp-0.4.0/validation/decider-2b/prompt.json +324 -0
  113. noulxp-0.4.0/validation/decider-2b/token-ids.json +1 -0
  114. noulxp-0.4.0/validation/julia-1/calibration.json +9 -0
  115. noulxp-0.4.0/validation/julia-1/check-coreml.json +110 -0
  116. noulxp-0.4.0/validation/julia-1/check-cpu.json +109 -0
  117. noulxp-0.4.0/validation/julia-1/conformance.jsonl +52 -0
  118. noulxp-0.4.0/validation/julia-1/odxp.json +86 -0
  119. noulxp-0.4.0/validation/julia-1/parity-cpu.json +1 -0
  120. noulxp-0.4.0/validation/julia-1/template.json +69 -0
  121. noulxp-0.4.0/validation/julia-1-official/calibration.json +9 -0
  122. noulxp-0.4.0/validation/julia-1-official/check-cpu.json +109 -0
  123. noulxp-0.4.0/validation/julia-1-official/conformance.jsonl +52 -0
  124. noulxp-0.4.0/validation/julia-1-official/odxp.json +92 -0
  125. noulxp-0.4.0/validation/julia-1-official/parity-cpu.json +1 -0
  126. noulxp-0.4.0/validation/julia-1-official/template.json +69 -0
  127. noulxp-0.4.0/validation/laya-main/calibration.json +66 -0
  128. noulxp-0.4.0/validation/laya-main/check-cpu.json +109 -0
  129. noulxp-0.4.0/validation/laya-main/conformance.jsonl +52 -0
  130. noulxp-0.4.0/validation/laya-main/odxp.json +86 -0
  131. noulxp-0.4.0/validation/laya-main/template.json +69 -0
  132. noulxp-0.4.0/validation/laya-multilingual/calibration.json +20 -0
  133. noulxp-0.4.0/validation/laya-multilingual/check-cpu.json +109 -0
  134. noulxp-0.4.0/validation/laya-multilingual/conformance.jsonl +52 -0
  135. noulxp-0.4.0/validation/laya-multilingual/odxp.json +86 -0
  136. noulxp-0.4.0/validation/laya-multilingual/template.json +69 -0
  137. noulxp-0.4.0/validation/laya-typed-decisions/calibration.json +66 -0
  138. noulxp-0.4.0/validation/laya-typed-decisions/check-cpu.json +109 -0
  139. noulxp-0.4.0/validation/laya-typed-decisions/conformance.jsonl +52 -0
  140. noulxp-0.4.0/validation/laya-typed-decisions/odxp.json +86 -0
  141. noulxp-0.4.0/validation/laya-typed-decisions/template.json +69 -0
@@ -0,0 +1,14 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ *.egg-info/
6
+ build/
7
+ dist/
8
+ .venv/
9
+ # Packages are large: they live outside the repository.
10
+ /packages/
11
+ *.onnx
12
+ *.onnx.data
13
+ *.gguf
14
+ *.safetensors
@@ -0,0 +1,165 @@
1
+ # Changelog
2
+
3
+ ## 0.4.0 (2026-09-30)
4
+
5
+ - **OpenDXP is now NoulXP.** Another open-source project already used the name
6
+ OpenDXP, so the standard, the package, the command and the badge take a name
7
+ of their own: `pip install noulxp`, `noulxp serve`, `noulxp check`, the
8
+ manifest `noulxp.json`, the versions `noulxp/0.1` and `noulxp/0.2`, and the
9
+ badge "NoulXP compatible". The format, the answers and the protocol are those
10
+ of 0.3.1.
11
+ - Packages made before the rename run as they did: a runtime reads `odxp.json`
12
+ when a package has no `noulxp.json`, and `odxp/0.1` and `odxp/0.2` as the same
13
+ versions under the old name. Converters write only the new names.
14
+ - The HTTP binding's header is `NoulXP-Version` (was `OpenDXP-Version`) and the
15
+ MCP server calls itself `noulxp`. `NOULXP_TOKEN` replaces `OPENDXP_TOKEN`,
16
+ which is still read.
17
+ - `opendxp` on PyPI is now a redirect: `pip install opendxp` installs `noulxp`,
18
+ `import opendxp` (and every `opendxp.*` module) is `noulxp`, and the `opendxp`
19
+ command runs `noulxp`, each with a note to switch.
20
+
21
+ ## 0.3.1 (2026-09-30)
22
+
23
+ - **Fixed: `opendxp serve` answered no faster than ~40 ms a request on Linux.**
24
+ An answer's headers and body left in two writes with Nagle's algorithm on, so
25
+ on a kept-alive connection the body waited for the client's delayed ACK of
26
+ the headers. Answers now leave in one write, with `TCP_NODELAY`. Julia 1 on an
27
+ NVIDIA A40, one client: 27.6 decisions/s (p50 62 ms) before, 161.6 (p50
28
+ 7.5 ms) after. macOS was not affected.
29
+ - **`opendxp serve` reads the requests that wait together** (`--batch N`,
30
+ default 32; 1 answers one at a time as before). A request that finds its
31
+ model idle is read at once, in its own thread, as `predict` reads it; requests
32
+ that arrive while the model reads queue, and one thread reads them together
33
+ (`predict_many`). On an A40 at 64 clients, Julia 1 answered 262 decisions/s
34
+ (78 one at a time; 300 with fused attention), Laya multilingual 191 (87);
35
+ one client, 178 (p50 6 ms). `--batch-rows` decodes a causal-letters
36
+ package's rows together (AnyJev at one client: p50 119 to 66 ms).
37
+ - The server's listen backlog is 128; it was socketserver's 5, past which a
38
+ burst of new clients was reset.
39
+ - `opendxp bench`'s HTTP client turns Nagle's algorithm off, as curl, requests
40
+ and browsers do: http.client sends a request's headers and body apart.
41
+ - Requests are validated with one compiled validator per schema, not a new one
42
+ per request.
43
+ - **`opendxp export --opset 23`** (laya, julia): attention as ONNX's fused
44
+ `Attention` operator instead of a chain of small ones, with the mask expanded
45
+ to the shape onnxruntime's kernels take. On an A40 through onnxruntime 1.30,
46
+ 6 to 14% more decisions per second one request at a time (Julia 1: 199 to
47
+ 210); on an x86 server CPU 1.6 to 2.0 times; on an Apple M4 CPU, Julia 1 from
48
+ 26.1 to 30.9. onnxruntime runs the operator on CUDA from 1.30: the runtime
49
+ refuses such a package on CUDA with an older onnxruntime rather than let
50
+ attention fall back to the CPU. encoder-markers reads the opset from the
51
+ manifest's weights entry.
52
+
53
+ ## 0.3.0 (2026-09-30)
54
+
55
+ - **`opendxp calibrate`** (SPEC.md 7.1): fits a package's temperatures, one per
56
+ question type, to labelled requests (an option's key, a distribution, or an
57
+ answer object per question) and writes a calibration.json, with accuracy, KL,
58
+ Brier and ECE before and after, on the labels and on `--test`. The model is
59
+ read once; each temperature is the least mean KL(label || answer), from a
60
+ log-spaced grid refined by golden-section search. On the typed-decisions test
61
+ split, 50 held-out requests labelled with one option each brought Julia 1's
62
+ mean confidence from 0.96 to 0.72, its accuracy (ECE 0.236 to 0.044), and the
63
+ benchmark's distributions took its KL from the gold from 2.78 to 0.23, with
64
+ the same decisions. `--hard-labels` fits to each label's leading option, to
65
+ how often the model is right, when the labels are distributions.
66
+ - **`--calibration FILE`** on `run`, `serve`, `mcp` and `bench`, and
67
+ `load(..., calibration=...)`: answer with another calibration.json than the
68
+ package's. The package's conformance file is still replayed at its own
69
+ (`conformance.replay` included), discovery names the file by its sha256, and
70
+ reports say `"calibration": "package"` otherwise.
71
+ - Runtimes have `readouts(items)` (each request read once, answered at any
72
+ calibration) and `distributions(..., calibration)`.
73
+ - Fixed: the cache of content-free priors was keyed without the temperature, so
74
+ a runtime answering at two calibrations reused a prior read at the other.
75
+
76
+ - **`opendxp bench`**: how fast a package (the reference runtime, in this
77
+ process) or any OpenDXP server (over the HTTP binding, at several concurrency
78
+ levels) answers: requests and decisions per second, latency percentiles,
79
+ errors by status and, with `--usd-per-hour`, the cost per 1,000 decisions.
80
+ Its clients back off on 429 and 503 as real ones do. Standard library only.
81
+ - **Rows read together** (causal-letters): `batch_rows` decodes up to that many
82
+ rows (a request's questions and rotations) in one llama.cpp call, each its
83
+ own sequence from empty memory; `batch_cache` shares one cache among them or
84
+ gives each its own (`per-row`). It is a serving choice, not part of a package:
85
+ `opendxp check --batch-rows N` says whether it keeps a package compatible on
86
+ a given machine. `predict_many` answers several requests' rows together.
87
+ - A request's rows are planned before any is read (typed layouts included),
88
+ then combined; the cache of content-free priors is never filled from a
89
+ planning pass.
90
+ - **Requests read together** (encoder-markers): `predict_many` runs several
91
+ requests' questions through the graph in passes of rows of similar length
92
+ (within 1.25 times the shortest, plus 16 tokens; at most 32 rows). Padding is
93
+ masked, so each answer is the one `predict` gives. On an NVIDIA A40, Julia 1's
94
+ 89 conformance rows take 243 ms together against 625 ms one at a time; a pass
95
+ filled up to a token budget instead padded short rows to a long one's length
96
+ and was slower than no batching. Static-shape providers (Core ML) answer one
97
+ at a time as before.
98
+ - The ONNX exporter names the graph's dimensions (`batch`, `tokens`, `options`)
99
+ instead of declaring `torch.export.Dim` ranges, which torch 2.8 refused for
100
+ models that treat a dimension of 1 specially.
101
+ - **No silent fallbacks.** An onnxruntime provider asked for by name that does
102
+ not load (a CUDA 13 build on CUDA 12, say) is an error instead of a run on the
103
+ CPU, and reports list the providers that loaded. A causal-letters run on the
104
+ CPU stays there: a GPU build of llama.cpp no longer offloads its matrix
105
+ products (`op_offload`).
106
+ - The encoder converters refuse transformers older than 5.2: 4.57 computes
107
+ ModernBERT differently, and its packages exported without an error, agreed
108
+ with the model they were traced from, and failed their conformance files.
109
+ - **Precision** (SPEC.md 8.1): `precision="exact"` (`--precision exact` on
110
+ check, bench, serve and mcp) asks a GPU for float32 products: ONNX Runtime's
111
+ CUDA provider without TF32, llama.cpp's CUDA and HIP backends accumulating
112
+ F16 and BF16 products in float32. On an NVIDIA A40 every encoder package then
113
+ gives the CPU's numbers (5e-5 instead of up to 0.0067), and AnyJev from its
114
+ BF16 weights passes its conformance file (0.0082 instead of 0.049),
115
+ costing 1 to 50 % of the speed. Reports name the precision they ran at.
116
+ - `conformance.replay` runs a package's conformance file through a runtime that
117
+ is already loaded, so an engine can check a package as it serves it.
118
+ - `opendxp bench` clients keep their connection open between requests, as SDKs
119
+ do.
120
+ - `scripts/prefix_sharing.py` works for every layout: it keeps the last row in
121
+ the cache and decodes only the tokens a row adds.
122
+
123
+ ## 0.2.0
124
+
125
+ OpenDXP 0.2: a wire protocol, and models asked in rotation. Every 0.1 package
126
+ still runs, and converters keep writing `odxp/0.1` unless a package needs 0.2.
127
+
128
+ - **HTTP binding** (SPEC.md 11): `POST /v1/systemone`, `GET /v1/models` with
129
+ each model's conformance summary, `GET /healthz`, an error schema, bearer
130
+ tokens and CORS by named origin. `opendxp serve` implements it with the
131
+ standard library and refuses to listen beyond the machine without a token.
132
+ - **MCP binding** (SPEC.md 12): `opendxp mcp` gives every package to AI agents
133
+ as a tool over stdio, with the request and response schemas as its input and
134
+ output schemas. It speaks the current MCP revision (per-request metadata,
135
+ `server/discover`) and the `initialize` handshake of earlier clients.
136
+ - **Typed layouts** (SPEC.md 6.7): a causal-letters prompt can lay out each
137
+ question type on its own (labels by position or attached to their options,
138
+ a listing, a legend for descriptions), inside the model's chat template, each
139
+ row encoded whole. States can be rendered as indented JSON or as a
140
+ conversation, with a text for an empty state (3.2).
141
+ - **Rotations** (SPEC.md 6.8): a question asked once per cyclic shift of its
142
+ options, the shifts combined by log-mean or mean, with an optional
143
+ content-free prior divided out first.
144
+ - `opendxp export anyjev` and `--runtime anyjev`: AnyJev (Nokia) at L0, any
145
+ instruction-tuned model asked the way anyjev 0.2.0 asks it, checked against
146
+ anyjev's own Decider. Validated on Qwen3-1.7B (VALIDATION.md).
147
+ - Packages declare `odxp/0.1` or `odxp/0.2`; this implementation runs both,
148
+ and `opendxp validate` checks that a package's files declare its manifest's
149
+ version.
150
+
151
+ ## 0.1.0
152
+
153
+ The first release of OpenDXP, the Open Decision Exchange Protocol, and its
154
+ reference implementation.
155
+
156
+ - The standard, `odxp/0.1`: packages (`odxp.json` and the files it names by
157
+ SHA-256), the `encoder-markers` and `causal-letters` profiles, calibration,
158
+ conformance files and compatibility levels, with JSON Schemas for every file.
159
+ - Reference runtimes on ONNX Runtime and llama.cpp (CPU and GPU builds).
160
+ `--device auto` uses CUDA when available and the CPU otherwise; Core ML,
161
+ OpenVINO, QNN and DirectML are used when named.
162
+ - `opendxp export` for Laya, Julia 1 and Decider; `opendxp conformance
163
+ generate` from the models' own code; `opendxp check`, `validate`, `run` and
164
+ `info`.
165
+ - The OpenDXP 0.1 request set: 52 requests, 91 questions, 11 languages.
@@ -0,0 +1,50 @@
1
+ # Contributing to NoulXP
2
+
3
+ NoulXP is a standard first and code second. A change to what a package means
4
+ is a change to SPEC.md, the schemas and the reference runtime together.
5
+
6
+ ## What is most useful
7
+
8
+ - **A model the profiles cannot describe.** Open an issue with the model, the
9
+ exact input construction of its own code, and what `template.json` or
10
+ `prompt.json` would need. This is how the standard grows.
11
+ - **A converter** for another model family (Von, open-jev, Kev, Nimble, ...):
12
+ `src/noulxp/export/<family>.py`, a native adapter in `src/noulxp/native/`
13
+ that runs the model's own code, and the validation results.
14
+ - **Another engine.** The standard is language-neutral. An engine in Rust,
15
+ Go or JavaScript that passes the conformance files of the published
16
+ packages is the strongest test of the spec; report where SPEC.md was
17
+ ambiguous.
18
+ - **Backend results**: `noulxp check --device <backend>` on hardware we do not
19
+ have (CUDA, OpenVINO, QNN, DirectML, ROCm), with the report JSON.
20
+
21
+ ## Rules for changes
22
+
23
+ 1. **Normative changes** (anything in SPEC.md that uses MUST, SHOULD or MAY,
24
+ the schemas, `src/noulxp/spec.py`) need an issue first, and bump the
25
+ standard's version when they change what an existing package means.
26
+ 2. **Conformance files are generated by the model's own code**, never by the
27
+ reference runtime, and record which code and versions produced them. A
28
+ converter that needed model-specific logic in the runtime to pass has not
29
+ passed: move that logic into the declarative files or propose a spec change.
30
+ 3. **No invented numbers.** VALIDATION.md and the paper report only what a
31
+ report file in the repository or the packages supports.
32
+ 4. **Nothing in a package executes.** No templates with logic, no pickles, no
33
+ `trust_remote_code`.
34
+
35
+ ## Development
36
+
37
+ ```bash
38
+ python -m venv .venv && . .venv/bin/activate
39
+ pip install -e ".[dev,onnx]"
40
+ pytest # no downloads; a tokenizer is built in memory
41
+ ruff check src tests scripts && ruff format --check src tests scripts
42
+ ```
43
+
44
+ The request set lives in `scripts/build_requests.py`; run it to regenerate
45
+ `src/noulxp/data/requests-0.1.jsonl`. Changing it changes the standard's
46
+ conformance set: packages then need their conformance files regenerated.
47
+
48
+ Commits: one logical change each, with a message that says what changed and
49
+ why. Sign-off is not required. By contributing you agree that your
50
+ contribution is licensed under Apache-2.0.
noulxp-0.4.0/LICENSE ADDED
@@ -0,0 +1,176 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
noulxp-0.4.0/NOTICE ADDED
@@ -0,0 +1,21 @@
1
+ NoulXP, the open exchange protocol for decision models
2
+ Copyright 2026 System One Models
3
+
4
+ This product includes software derived from:
5
+
6
+ - laya (convaiinnovations/laya), Apache-2.0: the input construction reproduced
7
+ declaratively in the Laya converter (laya/common.py build_sequence,
8
+ render_options, clamp_temperature, temp_bucket).
9
+ - Julia 1 (SupersonicLabs/Julia-1, revision a85b127321d580d65176c89ced8273f305745d85),
10
+ Apache-2.0: the JuliaDecisionModel inference subset in noulxp/export/julia.py
11
+ (julia/model.py), the input construction reproduced in its template
12
+ (julia/data.py sequence()), and its inference path rebuilt in
13
+ noulxp/native/julia.py (model.py, data.py, router/engine.py, typed.py).
14
+ - Decider (github.com/Mapika/decider at 23579f7a, decider-ai 1.6.0), Apache-2.0:
15
+ the prompt layout, labels, isolated score levels and temperatures reproduced
16
+ declaratively in the Decider converter (decider/prompt.py, prompt_fast.py,
17
+ systemone.py, temperature.py, engine_gguf.py), and its request rendering,
18
+ prompt construction and GGUF readout rebuilt in noulxp/native/decider.py.
19
+
20
+ NoulXP packages converted from these models carry the models' own weights and
21
+ remain under the models' licences.
noulxp-0.4.0/PAPER.md ADDED
@@ -0,0 +1,74 @@
1
+ # Paper outline
2
+
3
+ Working title: **NoulXP: a portable runtime standard for calibrated
4
+ single-pass decision models**
5
+
6
+ This file tracks the paper: its argument, its sections, and which experiments
7
+ are done. Numbers come from [VALIDATION.md](VALIDATION.md) and are measured,
8
+ never estimated.
9
+
10
+ ## The argument
11
+
12
+ 1. A new kind of model answers typed questions about a state (choose one of
13
+ these options, score on this scale, is this true) with a calibrated
14
+ probability for every option, in one forward pass. Laya, Julia 1 and
15
+ Decider are three of them, from three teams, with three architectures.
16
+ 2. Each ships its own inference code. Running, deploying or comparing them
17
+ means installing, trusting and wrapping each one. Formats such as ONNX and
18
+ GGUF carry the weights but not the rest: how the input is built from the
19
+ request, where the answer is read, how it is calibrated.
20
+ 3. NoulXP puts the rest in the package as data (a template or a prompt, and
21
+ temperatures), so one engine runs every model with no code written for it
22
+ and nothing from the package executed.
23
+ 4. Faithfulness is testable: the package carries a conformance file of what
24
+ the model's own code answers, and an engine passes when it reproduces it
25
+ within 0.01 with the same decisions.
26
+ 5. Two profiles cover the models we know of: bidirectional encoders that
27
+ score a marker token per option, and causal language models read at an
28
+ answer letter.
29
+
30
+ ## Sections
31
+
32
+ 1. **Introduction.** System One models; the cost of one runtime per model;
33
+ the contribution: the standard, the conformance method, a reference
34
+ implementation and three conversions.
35
+ 2. **Background.** Calibrated classification and temperature scaling (Guo et
36
+ al., 2017); marker-based encoders (Laya, Julia 1); letter-probability
37
+ readout from language models (Decider); ONNX and GGUF as weight formats;
38
+ loading model code at run time (`trust_remote_code`) and why a standard
39
+ should not need it.
40
+ 3. **The standard.** Requests and answers; packages with nothing executable;
41
+ the two profiles; calibration as data; conformance files and the
42
+ comparison rule; compatibility levels.
43
+ 4. **Reference implementation.** Runtimes for both profiles on ONNX Runtime
44
+ and llama.cpp; converters that copy no weights (ONNX external data pointing
45
+ into the published safetensors); conformance generation from the models'
46
+ own code; the System One Engine and the registry, which run packages and
47
+ show the badge.
48
+ 5. **Evaluation.** The experiments below.
49
+ 6. **Discussion.** What the tolerance means; backend numerics; sharing
50
+ computation versus exactness; what 0.1 leaves open (SPEC.md 14).
51
+ 7. **Conclusion.**
52
+
53
+ ## Experiments
54
+
55
+ | # | Question | Status |
56
+ | --- | --- | --- |
57
+ | E1 | Do packages reproduce their models' own answers on a CPU? | **Done.** 6 packages, 3 families, 52/52 cases each, all decisions equal, differences at the 4-decimal rounding floor. |
58
+ | E2 | Does a declarative input description give the same tokens as the models' own code? | **Done.** Decider: 149/149 rows identical. Laya and Julia: input construction tested against their own code (tests/). |
59
+ | E3 | Can a model's own ONNX export become a package without exporting again? | **Done.** Julia 1's published graph, renamed and pointed at its checkpoint: 52/52, and 100/100 on its authors' parity cases. |
60
+ | E4 | Do packages stay faithful on accelerator backends? | **Partly.** Julia on Core ML passes (level 3). Decider on Metal keeps every decision but not the probabilities (max 0.036). CUDA, ROCm, OpenVINO, QNN, DirectML to do. |
61
+ | E5 | What moves a causal model's probabilities? | **Done on one machine.** Flash attention off on the CPU: up to 0.014. Metal: 0.036 whatever the attention or cache settings. |
62
+ | E6 | What does sharing the prompt prefix cost in faithfulness? | **Done on one machine.** CPU: 1.8x faster over the set, up to 0.018, and one near-tie decision of 91 changed; Metal: 1.6x faster, nothing added. To repeat on an x86 server. |
63
+ | E7 | What does a package cost? | **Done on one machine.** 3 to 5 MB of graph per encoder package, no weight copies; load and latency per backend. Server numbers to do. |
64
+ | E8 | Is 0.01 the right tolerance? | **To do.** The distribution of differences, and of decision flips, across backends and quantisations. |
65
+ | E9 | Can someone else convert a model from the spec alone? | **To do.** A Laya Studio fine-tune and a model from outside the three families, converted by their authors. |
66
+
67
+ ## Before submission
68
+
69
+ - E4, E6 and E7 on an x86 server CPU and at least one NVIDIA GPU.
70
+ - E8 with quantised variants (int8 ONNX, Q4 GGUF) as derived packages.
71
+ - E9 with at least one team outside System One Models.
72
+ - Credit and permission: the model authors (Convai Innovations, Supersonic
73
+ Labs, Mark Marosi) are named for their models and asked before their
74
+ results are published.