Add TypeSafe SDK serving with verified low/high vLLM routes

#1
by gump2049 - opened
README.md CHANGED
@@ -215,6 +215,45 @@ Keep `enable_thinking=False` for these short decision outputs. The original exam
215
 
216
  This example returns standard chat-completion text through `choices[0].message.content`. It demonstrates **full-depth text serving**, not the specialized candidate-probability `/decide` gateway or `effort` routing. It does not enforce the three candidate labels; application-specific decision serving requires the corresponding prompt/compiler and output constraints. The server binds to localhost by default. Blackwell JIT compilation requires a compatible CUDA toolchain; the launcher disables the FlashInfer sampler as in the tested engine configuration.
217
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  ## 6. Reproducing the Evaluation
219
 
220
  Use the fixed dataset revision `7a63c3d6df2bdc6c8e82dfc566800dc318f0c36b`, preserve question and candidate order, and record the model revision, runtime, dtype, and compute budget. Keep reference labels out of the model input. Report decision accuracy separately from latency, and state precisely where timing starts and ends.
 
215
 
216
  This example returns standard chat-completion text through `choices[0].message.content`. It demonstrates **full-depth text serving**, not the specialized candidate-probability `/decide` gateway or `effort` routing. It does not enforce the three candidate labels; application-specific decision serving requires the corresponding prompt/compiler and output constraints. The server binds to localhost by default. Blackwell JIT compilation requires a compatible CUDA toolchain; the launcher disables the FlashInfer sampler as in the tested engine configuration.
217
 
218
+ ### TypeSafe SDK API with real low/high vLLM backends
219
+
220
+ The additional [TypeSafe deployment package](deployment/README.md) exposes `POST /v1/systemone` and `GET /v1/models` for the official `typesafe-sdk==0.7.0`. It routes `high` to the original merged **9B-5949 / 32-layer** model and `low` to a verified **16-layer** export of the same trained checkpoint. The original `serve_vllm.py` remains a standard, full-depth OpenAI text endpoint.
221
+
222
+ Follow [deployment/README.md](deployment/README.md) to download a pinned HF snapshot, export the trained low-depth model, and launch the two engines and gateway. The gateway checks actual model paths and layer configurations at startup; accepting an effort field alone is not treated as proof of shallow execution.
223
+
224
+ ```python
225
+ import os
226
+ from typesafe_sdk import Choice, RetryPolicy, TypeSafeClient
227
+
228
+ with TypeSafeClient(
229
+ base_url="http://127.0.0.1:30013", # API root; do not append /v1
230
+ api_key=os.environ["JEV_API_KEY"], # Your local gateway key
231
+ model="APUS-OpenJev-v1-9B",
232
+ retry=RetryPolicy(max_retries=0),
233
+ timeout=60,
234
+ ) as client:
235
+ result = client.system_one(
236
+ state="I was charged twice for my order. Please refund the duplicate charge.",
237
+ questions={"route": Choice(
238
+ instructions="Select the team that should handle this request.",
239
+ criteria={
240
+ "billing": "Payments, refunds, and billing issues",
241
+ "technical_support": "Technical problems and product errors",
242
+ "sales": "Pricing inquiries and purchase assistance",
243
+ },
244
+ )},
245
+ extra_body={"effort": "low"}, # Use high for 32 layers; default is high
246
+ )
247
+ print(result.choices["route"].choice)
248
+ print(result.raw_http_response.headers["x-openjev-executed-layers"])
249
+ ```
250
+
251
+ For the **official hosted TypeSafe service**, use `base_url="https://api.typesafe.ai"`, `api_key=os.environ["TYPESAFE_API_KEY"]`, and an official model such as `model="jev-1.13.0"`. Omit `extra_body={"effort": ...}`: this depth control is an APUS extension. The SDK context manager keeps connections reusable across calls. Never reuse a hosted API credential as a local gateway secret.
252
+
253
+ Compatibility covers synchronous/asynchronous SDK calls, typed Choice/Noul/Score responses, model listing, and documented error handling within the model's [supported limits](deployment/README.md#contract-and-limits). This does not claim identical model behavior or official probability calibration. Multi-question calls are currently evaluated separately. Two-engine depth routing uses additional memory and does not share KV state or automatically continue a shallow request into deeper layers.
254
+
255
+ See [deployment validation](deployment/VALIDATION.md) for measured results and limitations. The deployment directory retains its source project's [Apache 2.0 license](deployment/LICENSE); this does not change the model weights' licenses.
256
+
257
  ## 6. Reproducing the Evaluation
258
 
259
  Use the fixed dataset revision `7a63c3d6df2bdc6c8e82dfc566800dc318f0c36b`, preserve question and candidate order, and record the model revision, runtime, dtype, and compute budget. Keep reference labels out of the model input. Report decision accuracy separately from latency, and state precisely where timing starts and ends.
deployment/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
deployment/README.md ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # APUS TypeSafe SDK gateway with explicit vLLM low/high routing
2
+
3
+ This directory adds the **official `typesafe-sdk==0.7.0` request/response protocol** to APUS-OpenJev. The local service is an APUS model, not the hosted Jev service. Its probabilities, calibration, quality, and billing do not inherit the hosted service's guarantees.
4
+
5
+ Two separately loaded vLLM engines serve the same trained checkpoint at different layer depths:
6
+
7
+ | Effort | Engine | Execution |
8
+ | --- | --- | --- |
9
+ | `high` (default) | Original merged `9B-5949` | 32 layers |
10
+ | `low` | Verified prefix export of that same checkpoint | 16 layers, original final norm and LM head |
11
+
12
+ This is **explicit request routing between two engines**. It is not automatic confidence-based early exit, shared weights between processes, hidden-state continuation, KV broadcast, or candidate-only LM-head execution. Both vLLM engines compute the full output head; the gateway extracts the requested candidate scores. A client-requested `low` never silently falls back to `high`.
13
+
14
+ ## 1. Install and download the model
15
+
16
+ Use Linux, Python 3.12, CUDA-capable hardware, and vLLM 0.29.0. The 9B deployment work targets one RTX PRO 6000 Blackwell 96 GB; other hardware needs its own memory and numerical checks. Do not run the reference native runtime's Transformers 5.16.1 requirements in this vLLM environment.
17
+
18
+ ```bash
19
+ python3.12 -m venv .venv
20
+ source .venv/bin/activate
21
+ pip install vllm==0.29.0 transformers==5.17.0 huggingface-hub==1.32.0 ninja==1.13.2
22
+ pip install fastapi==0.128.8 uvicorn==0.40.0 httpx==0.28.1 httpx2==2.13.0 typesafe-sdk==0.7.0
23
+ ```
24
+
25
+ On Blackwell, configure the compatible CUDA 13 compiler in the same environment:
26
+
27
+ ```bash
28
+ pip install nvidia-cuda-nvcc==13.4.92 nvidia-cuda-crt==13.4.92 nvidia-cuda-cccl==13.3.4.3.1
29
+ export CUDA_HOME="$(python -c 'import sysconfig; print(sysconfig.get_path("purelib") + "/nvidia/cu13")')"
30
+ export PATH="$CUDA_HOME/bin:$PATH"
31
+ "$CUDA_HOME/bin/nvcc" --version
32
+ ```
33
+
34
+ The launch script disables the FlashInfer sampler to match the tested configuration. After downloading this directory, `pip install -r requirements.txt` installs the pinned deployment dependencies. `requirements-test.txt` supports the CPU gateway tests without installing a GPU runtime.
35
+
36
+ Resolve the repository revision once, then download by that immutable revision. The repository is a family bundle: use the **model subdirectory**, not its root.
37
+
38
+ ```bash
39
+ export APUS_REVISION="$(python -c 'from huggingface_hub import HfApi; print(HfApi().model_info("apus-ailab/APUS-OpenJev-v1").sha)')"
40
+ hf download apus-ailab/APUS-OpenJev-v1 --revision "$APUS_REVISION" \
41
+ --include '9B-5949/*' 'deployment/*' --local-dir ./APUS-OpenJev-v1
42
+ cd ./APUS-OpenJev-v1/deployment
43
+ export APUS_FULL_MODEL="$(cd ../9B-5949 && pwd)"
44
+ ```
45
+
46
+ If you already downloaded the deployment code separately, the included model-only helper performs the same immutable-revision resolution and prints the resulting model path:
47
+
48
+ ```bash
49
+ python download_model.py --local-dir ../models --revision "$APUS_REVISION"
50
+ # With this layout, set APUS_FULL_MODEL to ../models/9B-5949.
51
+ ```
52
+
53
+ Create a **new separate directory** for the trained 16-layer prefix. The exporter refuses an existing destination and verifies retained tensors; it never modifies the original weights.
54
+
55
+ ```bash
56
+ export APUS_LOW_MODEL="$(dirname "$APUS_FULL_MODEL")/9B-5949-low16"
57
+ python export_low.py --source "$APUS_FULL_MODEL" --output "$APUS_LOW_MODEL"
58
+ ```
59
+
60
+ Start the full engine in terminal A, using an otherwise available GPU:
61
+
62
+ ```bash
63
+ export JEV_VLLM_PYTHON="$VIRTUAL_ENV/bin/python"
64
+ CUDA_VISIBLE_DEVICES=0 JEV_EFFORT=high \
65
+ JEV_MODEL_PATH="$APUS_FULL_MODEL" JEV_ENGINE_PORT=30002 \
66
+ JEV_GPU_MEMORY_UTILIZATION=0.70 JEV_MAX_NUM_SEQS=8 \
67
+ bash run_backend.sh
68
+ ```
69
+
70
+ In terminal B, activate the same environment, set the same `CUDA_HOME` and `APUS_LOW_MODEL` paths, and start the shallow engine:
71
+
72
+ ```bash
73
+ export JEV_VLLM_PYTHON="$VIRTUAL_ENV/bin/python"
74
+ CUDA_VISIBLE_DEVICES=0 JEV_EFFORT=low \
75
+ JEV_MODEL_PATH="$APUS_LOW_MODEL" JEV_ENGINE_PORT=30004 \
76
+ JEV_GPU_MEMORY_UTILIZATION=0.22 JEV_MAX_NUM_SEQS=8 \
77
+ bash run_backend.sh
78
+ ```
79
+
80
+ These defaults allocate separate engine budgets of 70% and 22% of the same 96 GB GPU. They are not appropriate if another workload already occupies that GPU. Compilation can take several minutes. The launcher verifies metadata and weight hashes before starting; this adds sequential disk reads. In terminal C, check both engines:
81
+
82
+ ```bash
83
+ curl -f http://127.0.0.1:30002/health
84
+ curl -f http://127.0.0.1:30004/health
85
+ ```
86
+
87
+ Do not proceed until both are ready. Each foreground process can be stopped with Ctrl-C in its own terminal. Never reuse a port or GPU allocation belonging to another workload. The two engines must belong to the same source checkpoint; archive the export provenance and source revision together. The package uses `max_num_seqs=8` per engine; historical high-only measurements with 32 sequences are a different serving configuration and must not be copied as this setup's result.
88
+
89
+ ## 2. Start the SDK gateway
90
+
91
+ ```bash
92
+ export JEV_MODEL_PATH="$APUS_FULL_MODEL"
93
+ export JEV_HIGH_MODEL_PATH="$APUS_FULL_MODEL"
94
+ export JEV_LOW_MODEL_PATH="$APUS_LOW_MODEL"
95
+ export JEV_HIGH_URL=http://127.0.0.1:30002
96
+ export JEV_LOW_URL=http://127.0.0.1:30004
97
+ export JEV_HIGH_MODEL=apus-openjev-9b-high32
98
+ export JEV_LOW_MODEL=apus-openjev-9b-low16
99
+ export JEV_MODEL_NAME=APUS-OpenJev-v1-9B
100
+ # Supply your own local gateway secret; this is NOT your hosted TypeSafe API key.
101
+ export JEV_API_KEY='replace-with-your-local-gateway-secret'
102
+ python gateway.py --host 127.0.0.1 --port 30013
103
+ ```
104
+
105
+ Startup requires both model directories on the same filesystem as the gateway. It checks 32/16-layer configs, merged indexes, completed low-export provenance, source/export metadata hashes, and each `/v1/models` alias/root against its configured artifact directory. Mismatches fail startup. The tokenizer must come from the full model directory. Engine URLs and aliases must be distinct. Depth headers report the selected verified backend configuration, not per-request layer-hook telemetry; live numerical evaluation remains necessary.
106
+
107
+ The gateway binds to localhost by default. Configure an authenticated HTTPS proxy before exposing it publicly. Backend URLs are operator configuration, never accepted from incoming requests. Both backend connections and the SDK client can reuse HTTP connections.
108
+
109
+ ## 3. Use the official SDK against APUS
110
+
111
+ Use the **API root** as `base_url` — do not append `/v1`. The SDK appends `/v1/systemone` and `/v1/models` itself.
112
+
113
+ ```python
114
+ import os
115
+ from typesafe_sdk import Choice, Noul, RetryPolicy, TypeSafeClient
116
+
117
+ with TypeSafeClient(
118
+ base_url="http://127.0.0.1:30013",
119
+ api_key=os.environ["JEV_API_KEY"],
120
+ model="APUS-OpenJev-v1-9B",
121
+ retry=RetryPolicy(max_retries=0),
122
+ ) as client:
123
+ print(client.models.list())
124
+ result = client.system_one(
125
+ state={
126
+ "message": "My parcel arrived damaged yesterday. Can I get a refund?",
127
+ "policy": "Damaged items reported within 30 days qualify for a refund.",
128
+ },
129
+ questions={
130
+ "queue": Choice(
131
+ instructions="Choose the appropriate customer-support queue.",
132
+ criteria={"returns": "Returns and refunds", "billing": "Payments and invoices"},
133
+ ),
134
+ "eligible": Noul(instructions="Is the customer eligible under this refund policy?"),
135
+ },
136
+ extra_body={"effort": "low"},
137
+ )
138
+ print(result.answers["queue"].choice)
139
+ print(result.answers["eligible"].noul)
140
+ print(result.raw_http_response.headers["x-openjev-executed-layers"])
141
+ ```
142
+
143
+ Use `extra_body={"effort": "high"}` for the full model, or omit it because high is the default. **`effort` is an APUS extension**, not a promise that the hosted TypeSafe API accepts the same field.
144
+
145
+ Run the complete English customer-support example (Choice + Noul + ordinal Score):
146
+
147
+ ```bash
148
+ python sdk_example.py --effort high
149
+ python sdk_example.py --effort low
150
+ ```
151
+
152
+ Async uses the same fields:
153
+
154
+ ```python
155
+ import asyncio
156
+ import os
157
+ from typesafe_sdk import AsyncTypeSafeClient, Noul, RetryPolicy
158
+
159
+ async def main():
160
+ async with AsyncTypeSafeClient(
161
+ base_url="http://127.0.0.1:30013",
162
+ api_key=os.environ["JEV_API_KEY"],
163
+ model="APUS-OpenJev-v1-9B",
164
+ retry=RetryPolicy(max_retries=0),
165
+ ) as client:
166
+ result = await client.system_one(
167
+ state="The customer received a damaged item yesterday.",
168
+ questions={"damaged": Noul(instructions="Did the customer report a damaged item?")},
169
+ extra_body={"effort": "high"},
170
+ )
171
+ print(result.answers["damaged"].noul)
172
+
173
+ asyncio.run(main())
174
+ ```
175
+
176
+ ## 4. Use the official hosted TypeSafe service separately
177
+
178
+ This snippet sends a real request to the hosted service and can incur charges. Keep its credentials separate from the local gateway secret. It deliberately does not send the APUS `effort` extension.
179
+
180
+ ```python
181
+ import os
182
+ from typesafe_sdk import Choice, RetryPolicy, TypeSafeClient
183
+
184
+ with TypeSafeClient(
185
+ base_url="https://api.typesafe.ai",
186
+ api_key=os.environ["TYPESAFE_API_KEY"],
187
+ model="jev-latest",
188
+ retry=RetryPolicy(max_retries=0),
189
+ ) as client:
190
+ result = client.system_one(
191
+ state="The customer was charged twice for the same order.",
192
+ questions={"queue": Choice(
193
+ instructions="Choose the appropriate customer-support queue.",
194
+ criteria={"billing": "Payments and invoices", "shipping": "Delivery and tracking"},
195
+ )},
196
+ )
197
+ print(result.answers["queue"].choice)
198
+ ```
199
+
200
+ ## Contract and limits
201
+
202
+ - Supported SDK 0.7.0 methods: synchronous/asynchronous `system_one` and `models.list`. This SDK version exposes no `models.retrieve` method.
203
+ - `GET /v1/models` lists the APUS model and `jev-latest` as an explicitly labeled **local compatibility alias**. Responses name the actual APUS model.
204
+ - `POST /v1/systemone` accepts `state`, `questions`, `model`, and the optional APUS `effort` extension only. Unknown fields, invalid effort, unsupported models, malformed or duplicate-key JSON fail explicitly.
205
+ - State: string / object / array. Instructions: string / object / array / null. Choice descriptions also allow null; null uses the candidate name. JSON is serialized deterministically; ordinary string descriptions remain unchanged.
206
+ - Choice: 2–16 candidates. Noul: yes/no; optional true/false descriptions affect the question's meaning. Canonical true/false descriptions retain the original binary prompt. Custom descriptions are included explicitly.
207
+ - Score: 1–10 ordered levels, selected by their descriptions; returns the expected zero-based index, probabilities, and the original legend. This differs from the trained binary `score_level` task and has **not established ordinal-score quality or calibration**. A single-level Score has only one possible result: score 0, probability 1, no GPU request, zero model tokens, and executed-layers header 0. Its confidence 1 denotes a degenerate distribution, not evidence that the rubric is objectively correct.
208
+ - At most 16 questions, 1 MiB request body, and 8,191 compiled prompt tokens per question (one output token reserved). Inputs are never silently truncated. Maximum 32 in-flight requests, with a 120-second request deadline.
209
+ - Each question is compiled and scored separately. Usage sums the processed prompt tokens and one scoring token per question; `billing_units` means local decision count, not official monetary billing.
210
+ - Confidence is normalized distribution concentration. Candidate probabilities are uncalibrated conditional softmax; neither quantity inherits official calibration semantics.
211
+ - Response shape uses `model`, `answers`, `usage`; Choice has `choice/confidence/probabilities`, Noul has `noul`, Score has `score/confidence/probabilities/legend`.
212
+ - Headers `x-openjev-effort`, `x-openjev-executed-layers`, and `x-openjev-engine-model` record the chosen route. `x-typesafe-request-id` is a local request identifier.
213
+
214
+ ## Verification status
215
+
216
+ CPU integration tests use the **real official SDK 0.7.0**, both sync and async, through an ASGI gateway with a deterministic mocked inference backend. They verify protocol, routing, payload validation, and numerical response assembly; they are not GPU quality or performance measurements.
217
+
218
+ ```bash
219
+ pip install -r requirements-test.txt
220
+ python -m pytest -q tests/test_typesafe_gateway.py
221
+ ```
222
+
223
+ Before reporting GPU performance, record immutable model/export identities, prove 16/32-layer engine admission, run the same frozen requests through both routes, retain raw responses and collection timestamps, and compare against a reference using **the same compiled prompts**. In particular, a historic `score_level` record mapped to TypeSafe Noul changes its prompt primitive; do not silently compare it as byte-identical input.
224
+
225
+ Run a real service smoke test and preserve its raw requests, responses, UTC timestamps, and per-call latency:
226
+
227
+ ```bash
228
+ python smoke_sdk.py --base-url http://127.0.0.1:30013 --output ./sdk-smoke-new-run
229
+ ```
230
+
231
+ This sends five HTTP calls: models, high multi-question, low multi-question, an expected 422 for invalid effort, and an async low request. The two multi-question requests include all three primitives. Use a new output directory for every run. No authentication headers are saved.
232
+
233
+ Deployment code retains the included Apache 2.0 license and source notices. Model weights retain their applicable upstream licenses.
deployment/VALIDATION.md ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deployment validation — 2026-09-21
2
+
3
+ The official TypeSafe Python SDK **0.7.0** successfully called this gateway on a real GPU deployment. `high` reached a 32-layer vLLM engine; `low` reached a separately exported 16-layer engine. This is explicit depth selection, not automatic confidence routing.
4
+
5
+ ## Live SDK HTTP results
6
+
7
+ RTX PRO 6000 Blackwell 96 GB, vLLM 0.29.0, BF16, merged `9B-5949`, one GPU with two resident engines. Local loopback HTTP, one persistent SDK client, concurrency 1, retries disabled. Each effort ran the same frozen80 panel three times. Collection: **2026-09-21 13:46:20–13:46:52 UTC**. Timing covers SDK invocation through complete response validation; it is not streaming TTFT.
8
+
9
+ | SDK route | Successful | Accuracy per round | Mean | P50 | P95 | P99 |
10
+ | --- | ---: | --- | ---: | ---: | ---: | ---: |
11
+ | high / 32 layers | 240/240 | 67/80 in all three | 70.11 ms | 23.75 ms | 243.10 ms | 290.51 ms |
12
+ | low / 16 layers | 240/240 | 67/80 in all three | 49.17 ms | 22.01 ms | 154.61 ms | 177.58 ms |
13
+
14
+ Percentiles pool all 240 calls per route; they are not averages of per-round percentiles. Each run scored Browser 12/16, HelpSteer3 13/16, BoolQ 16/16, MNLI 12/16, and Score-derived Noul 14/16. There are only 80 distinct questions from 79 parent groups. These development questions have informed the project; neither significance nor general noninferiority follows from this result.
15
+
16
+ Mean latency fell **29.86%** (about **1.43×** speed) in this sequential local workload. Median improvement was much smaller. This does not establish a doubling of throughput or a public-network speedup. Serving budgets: high `gpu_memory_utilization=0.70`, `max_num_seqs=32`; low `0.22`, `max_num_seqs=8`; both max length 8192, prefix cache disabled. The supplied launcher defaults to 8 sequences for each route; its concurrent throughput must be benchmarked separately. Resident GPU memory was approximately 90,580 MiB with both engines loaded.
17
+
18
+ The SDK smoke separately passed model listing, multi-question Choice/Noul/Score at both efforts, invalid-effort error handling, and an asynchronous low request: **5/5 checks**. Full request/response bodies, UTC timestamps, route headers, and raw evaluation records were retained internally; no hosted API credentials are needed for this service.
19
+
20
+ ## Numerical verification and limits
21
+
22
+ The exporter retained **548 tensors**, preserving their bytes, including the original embedding, final norm and LM head, while removing decoder layers 16–31. The source weights and metadata were checked against their release manifest. A native reference run used layer hooks to confirm that only layers 0–15 ran for all 80 questions.
23
+
24
+ A separate raw vLLM candidate-scoring comparison used the **same original compiled prompts**:
25
+
26
+ | Route | Correct | Same selected candidate as native reference | Maximum probability difference |
27
+ | --- | ---: | ---: | ---: |
28
+ | high32 | 67/80 | 80/80 | 0.06422 |
29
+ | low16 | 67/80 | 78/80 | 0.08855 |
30
+
31
+ Low changed two Browser decisions: one incorrect answer became correct and one correct answer became incorrect. Equal aggregate accuracy does **not** establish identical decisions or numerical parity. Original probability thresholds must be recalibrated for the deployed runtime. The first raw low request took about 1,552 ms before warm execution; SDK measurements above followed raw verification and smoke requests.
32
+
33
+ The wire benchmark uses the same state, instructions, candidate descriptions and frozen IDs as the official API requests, with the APUS model and effort extension. Historic binary `score_level` records map to TypeSafe **Noul**, changing the internal prompt primitive; the raw-reference comparison and SDK-wire comparison therefore answer different questions. Ordinal Score protocol smoke is not a scored quality benchmark for ordinal rubrics.
34
+
35
+ ## Reproducibility
36
+
37
+ - Source HF family: `apus-ailab/APUS-OpenJev-v1`, model subdirectory `9B-5949/`; inspected source repository revision `c8a31769918d34e4142b9338b231a7bef59de3f3`. Serving-code publication does not change weights.
38
+ - Frozen panel SHA256: `d6dd45a9c8cb9f5b50254ac5f8f550e0ab1341e993c8657eb89232f64686ebf6`.
39
+ - Protocol tests: **12 passing**, real official SDK sync/async against a real ASGI gateway and mocked scoring engines.
40
+ - Export tests: **9 passing**, standalone CPU tiny-Qwen prefix/readout equivalence, retained tensor identity, source modification and weight-tampering rejection.
41
+ - Live collector tests: **2 passing**; independent offline verification checks all 480 saved requests, IDs, gold scoring, route headers and recorded timing.
42
+ - Serving bundle passes Ruff lint/format and shell syntax checks. A clean OS dependency installation was not tested; GPU tests used the existing verified Runpod environment.
43
+
44
+ The official hosted service was measured separately over a WAN: the earlier 240 pooled responses had P50/P95/P99 **280.80/376.56/830.45 ms**. A subsequent SDK single-connection 160-call test averaged **301.63 ms**. Those include geography/network effects and must not be divided by these loopback numbers to claim a model speedup.
45
+
46
+ ## Compatibility boundaries
47
+
48
+ The gateway supports the SDK's synchronous/asynchronous `system_one` and `models.list` methods, typed responses, local bearer auth and documented errors. See README for candidate, context and request limits. This release does not assert unlimited compatibility with every request accepted by the hosted service, official calibration, automatic early exit, shared KV broadcast, or production Browser task completion.
deployment/contracts.py ADDED
@@ -0,0 +1,301 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Wire validation and deterministic semantic bridge to existing decision prompts."""
2
+
3
+ import json
4
+ import math
5
+ from dataclasses import dataclass
6
+
7
+ PROMPT_VERSION = "jev.dynamic.prompt.v2"
8
+ LABELS = tuple("ABCDEFGHIJKLMNOP")
9
+ BINARY_CRITERIA = [
10
+ {"id": "yes", "description": "The stated proposition is true."},
11
+ {"id": "no", "description": "The stated proposition is false."},
12
+ ]
13
+
14
+
15
+ def validate_request(record):
16
+ for key in ("id", "group_id", "state", "instructions"):
17
+ if not isinstance(record.get(key), str) or not record[key].strip():
18
+ raise ValueError(f"{key} must be a nonempty string")
19
+ if record.get("primitive") not in ("choice", "noul", "score_level"):
20
+ raise ValueError("unsupported primitive")
21
+ criteria = record.get("criteria")
22
+ if not isinstance(criteria, list) or not 2 <= len(criteria) <= len(LABELS):
23
+ raise ValueError("criteria must contain 2..16 candidates")
24
+ ids = []
25
+ for candidate in criteria:
26
+ if not isinstance(candidate, dict):
27
+ raise TypeError("candidate must be an object")
28
+ for key in ("id", "description"):
29
+ if not isinstance(candidate.get(key), str) or not candidate[key].strip():
30
+ raise ValueError(f"candidate {key} must be nonempty")
31
+ ids.append(candidate["id"])
32
+ if len(set(ids)) != len(ids):
33
+ raise ValueError("duplicate candidate ids")
34
+ if record["primitive"] != "choice" and criteria != BINARY_CRITERIA:
35
+ raise ValueError("noul and score_level require canonical yes/no criteria")
36
+
37
+
38
+ def validate_record(record):
39
+ validate_request(record)
40
+ if record.get("gold") not in [c["id"] for c in record["criteria"]]:
41
+ raise ValueError("gold must be a candidate id")
42
+ if not isinstance(record.get("provenance"), dict):
43
+ raise TypeError("provenance must be an object")
44
+
45
+
46
+ def label_mapping(record):
47
+ validate_request(record)
48
+ return dict(zip(LABELS, (c["id"] for c in record["criteria"])))
49
+
50
+
51
+ def render_prompt_parts(record):
52
+ """Text prefix/suffix; callers MUST check tokenizer boundary equivalence."""
53
+ validate_request(record)
54
+ prefix = "Shared state:\n" + record["state"] + "\n\n"
55
+ task = {
56
+ "primitive": record["primitive"],
57
+ "instructions": record["instructions"],
58
+ "criteria": [
59
+ {"label": label, "description": candidate["description"]}
60
+ for label, candidate in zip(LABELS, record["criteria"])
61
+ ],
62
+ }
63
+ suffix = json.dumps(task, ensure_ascii=False, sort_keys=True)
64
+ suffix += (
65
+ "\nReturn only the selected letter: "
66
+ + ", ".join(LABELS[: len(record["criteria"])])
67
+ + ".\nAnswer:"
68
+ )
69
+ return prefix, suffix
70
+
71
+
72
+ def render_prompt(record):
73
+ return "".join(render_prompt_parts(record))
74
+
75
+
76
+ COMPILER_VERSION = "jev.typesafe.compiler.v1"
77
+ CONFIDENCE_POLICY = "normalized-entropy.v1"
78
+
79
+
80
+ class ServingError(Exception):
81
+ def __init__(self, status, code, message):
82
+ super().__init__(message)
83
+ self.status, self.code, self.message = status, code, message
84
+
85
+
86
+ def invalid(message):
87
+ raise ServingError(422, "invalid_request", message)
88
+
89
+
90
+ def text(value):
91
+ if isinstance(value, str) and value.strip():
92
+ return value
93
+ return json.dumps(value, ensure_ascii=False, sort_keys=True, allow_nan=False)
94
+
95
+
96
+ def content(value):
97
+ return isinstance(value, (str, dict, list))
98
+
99
+
100
+ @dataclass(frozen=True)
101
+ class Question:
102
+ key: str
103
+ kind: str
104
+ record: dict
105
+ legend: dict | None = None
106
+
107
+
108
+ def prepare_request(body, *, model_name, max_questions=16, allow_score=True):
109
+ if not isinstance(body, dict) or set(body) - {
110
+ "state",
111
+ "questions",
112
+ "model",
113
+ "effort",
114
+ }:
115
+ invalid("Expected state, questions, model and optional effort only")
116
+ if not isinstance(body.get("model", "jev-latest"), str) or body.get(
117
+ "model", "jev-latest"
118
+ ) not in {model_name, "jev-latest"}:
119
+ raise ServingError(404, "unknown_model", "Model is not served")
120
+ if not content(body.get("state")):
121
+ invalid("state must be a string, object or array")
122
+ questions = body.get("questions")
123
+ if not isinstance(questions, dict) or not 1 <= len(questions) <= max_questions:
124
+ invalid("questions must be a nonempty map within the configured limit")
125
+ result = []
126
+ for key, question in questions.items():
127
+ if not isinstance(key, str) or not key.strip():
128
+ invalid("Question IDs must be nonempty strings")
129
+ if not isinstance(question, dict) or set(question) - {
130
+ "type",
131
+ "instructions",
132
+ "criteria",
133
+ }:
134
+ invalid("Invalid question fields")
135
+ kind = question.get("type")
136
+ instruction = question.get("instructions")
137
+ if instruction is not None and not content(instruction):
138
+ invalid("instructions must be text, object, array or null")
139
+ instruction = (
140
+ text(instruction)
141
+ if instruction is not None
142
+ else "Evaluate the state against the supplied criteria."
143
+ )
144
+ criteria = question.get("criteria")
145
+ legend = None
146
+ internal_kind = kind
147
+ if kind == "choice":
148
+ if not isinstance(criteria, dict) or not 2 <= len(criteria) <= len(LABELS):
149
+ invalid(
150
+ "This model supports 2..16 Choice candidates; none are truncated"
151
+ )
152
+ candidates = []
153
+ for name, description in criteria.items():
154
+ if not isinstance(name, str) or not name.strip():
155
+ invalid("Choice names must be nonempty strings")
156
+ if description is not None and not content(description):
157
+ invalid("Invalid Choice description")
158
+ candidates.append(
159
+ {
160
+ "id": name,
161
+ "description": name
162
+ if description is None
163
+ else text(description),
164
+ }
165
+ )
166
+ elif kind == "noul":
167
+ if criteria is not None:
168
+ if not isinstance(criteria, dict) or set(criteria) - {"true", "false"}:
169
+ invalid("Noul criteria accepts true and false descriptions")
170
+ if any(v is not None and not content(v) for v in criteria.values()):
171
+ invalid("Invalid Noul criterion")
172
+ if any(v is not None for v in criteria.values()) and criteria != {
173
+ "true": BINARY_CRITERIA[0]["description"],
174
+ "false": BINARY_CRITERIA[1]["description"],
175
+ }:
176
+ instruction = text(
177
+ {"question": instruction, "meaning_of_answers": criteria}
178
+ )
179
+ candidates = [dict(c) for c in BINARY_CRITERIA]
180
+ elif kind == "score":
181
+ if not allow_score:
182
+ raise ServingError(
183
+ 422,
184
+ "unsupported_score",
185
+ "Ordinal Score is experimental and disabled; it is not score_level",
186
+ )
187
+ if not isinstance(criteria, list) or not 1 <= len(criteria) <= 10:
188
+ invalid("Score requires 1..10 ordered levels")
189
+ if any(not content(v) for v in criteria):
190
+ invalid("Invalid Score level")
191
+ legend = {str(i): v for i, v in enumerate(criteria)}
192
+ candidates = [{"id": k, "description": text(v)} for k, v in legend.items()]
193
+ internal_kind = "choice"
194
+ instruction = text(
195
+ {
196
+ "question": instruction,
197
+ "task": "Select the best matching ordered rubric level. Levels run from lowest to highest.",
198
+ }
199
+ )
200
+ else:
201
+ invalid("Unsupported question type")
202
+ record = {
203
+ "id": key,
204
+ "group_id": "serving",
205
+ "state": text(body["state"]),
206
+ "primitive": internal_kind,
207
+ "instructions": instruction,
208
+ "criteria": candidates,
209
+ }
210
+ result.append(Question(key, kind, record, legend))
211
+ return result
212
+
213
+
214
+ @dataclass(frozen=True)
215
+ class Compiled:
216
+ question: Question
217
+ prompt_ids: list[int]
218
+ candidate_token_ids: list[int]
219
+
220
+
221
+ def compile_question(question, tokenizer, max_tokens=8192):
222
+ if question.kind == "score" and len(question.record["criteria"]) == 1:
223
+ return Compiled(question, [], [])
224
+ prompt = tokenizer.apply_chat_template(
225
+ [{"role": "user", "content": render_prompt(question.record)}],
226
+ tokenize=False,
227
+ add_generation_prompt=True,
228
+ enable_thinking=False,
229
+ )
230
+ ids = tokenizer.encode(prompt, add_special_tokens=False)
231
+ if not ids or len(ids) > max_tokens:
232
+ invalid("Compiled prompt exceeds token budget; no truncation is allowed")
233
+ special = set(tokenizer.all_special_ids)
234
+ if special.intersection(
235
+ tokenizer.encode(render_prompt(question.record), add_special_tokens=False)
236
+ ):
237
+ invalid("Input contains reserved model control tokens")
238
+ tokens = []
239
+ for label in LABELS[: len(question.record["criteria"])]:
240
+ encoded = tokenizer.encode(label, add_special_tokens=False)
241
+ if (
242
+ len(encoded) != 1
243
+ or tokenizer.encode(prompt + label, add_special_tokens=False)
244
+ != ids + encoded
245
+ ):
246
+ invalid("Candidate is not one token at the answer boundary")
247
+ if encoded[0] in special or encoded[0] in tokens:
248
+ invalid("Invalid candidate token mapping")
249
+ tokens.append(encoded[0])
250
+ return Compiled(question, ids, tokens)
251
+
252
+
253
+ def probabilities(scores):
254
+ if len(scores) < 2 or any(
255
+ type(v) not in (int, float) or not math.isfinite(v) for v in scores
256
+ ):
257
+ raise ServingError(
258
+ 502, "invalid_distribution", "Backend returned invalid candidate scores"
259
+ )
260
+ shift = max(scores)
261
+ values = [math.exp(v - shift) for v in scores]
262
+ total = sum(values)
263
+ return [v / total for v in values]
264
+
265
+
266
+ def confidence(probs):
267
+ if len(probs) == 1:
268
+ return 1.0
269
+ entropy = -sum(p * math.log(p) for p in probs if p > 0)
270
+ return min(1.0, max(0.0, 1.0 - entropy / math.log(len(probs))))
271
+
272
+
273
+ def answer(question, probs):
274
+ keys = [c["id"] for c in question.record["criteria"]]
275
+ if (
276
+ len(keys) != len(probs)
277
+ or any(
278
+ type(p) not in (int, float) or not math.isfinite(p) or not 0 <= p <= 1
279
+ for p in probs
280
+ )
281
+ or not math.isclose(sum(probs), 1, abs_tol=1e-6)
282
+ ):
283
+ raise ServingError(
284
+ 502,
285
+ "invalid_distribution",
286
+ "Backend distribution does not match candidates",
287
+ )
288
+ if question.kind == "noul":
289
+ return {"type": "noul", "noul": probs[keys.index("yes")]}
290
+ value = {
291
+ "type": question.kind,
292
+ "probabilities": dict(zip(keys, probs)),
293
+ "confidence": confidence(probs),
294
+ }
295
+ if question.kind == "choice":
296
+ value["choice"] = keys[max(range(len(probs)), key=probs.__getitem__)]
297
+ else:
298
+ value.update(
299
+ score=sum(i * p for i, p in enumerate(probs)), legend=question.legend
300
+ )
301
+ return value
deployment/download_model.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Download only the merged 9B-5949 model from one immutable HF snapshot."""
2
+
3
+ import argparse
4
+ import json
5
+ from pathlib import Path
6
+ import re
7
+
8
+
9
+ def main():
10
+ p = argparse.ArgumentParser(description=__doc__)
11
+ p.add_argument("--repo", default="apus-ailab/APUS-OpenJev-v1")
12
+ p.add_argument("--revision", default="main")
13
+ p.add_argument("--local-dir", type=Path, required=True)
14
+ args = p.parse_args()
15
+ from huggingface_hub import HfApi, snapshot_download
16
+
17
+ revision = HfApi().model_info(args.repo, revision=args.revision).sha
18
+ if not re.fullmatch("[a-f0-9]{40}", revision):
19
+ raise ValueError("Hub did not return an immutable commit")
20
+ root = Path(
21
+ snapshot_download(
22
+ repo_id=args.repo,
23
+ revision=revision,
24
+ allow_patterns=["9B-5949/*"],
25
+ local_dir=args.local_dir,
26
+ )
27
+ )
28
+ model = root / "9B-5949"
29
+ config = json.loads((model / "config.json").read_text())
30
+ if (
31
+ config["text_config"]["num_hidden_layers"] != 32
32
+ or (model / "adapter_config.json").exists()
33
+ ):
34
+ raise ValueError("Expected the complete merged 32-layer model")
35
+ print(
36
+ json.dumps(
37
+ {
38
+ "repo": args.repo,
39
+ "revision": revision,
40
+ "model_path": str(model.resolve()),
41
+ }
42
+ )
43
+ )
44
+
45
+
46
+ if __name__ == "__main__":
47
+ main()
deployment/export_low.py ADDED
@@ -0,0 +1,354 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Export actual 16-layer Qwen3.5-9B prefix, preserving its shared norm/head."""
2
+
3
+ import argparse
4
+ import copy
5
+ from datetime import datetime, timezone
6
+ import hashlib
7
+ import json
8
+ from pathlib import Path
9
+ import re
10
+ import shutil
11
+
12
+ LAYER = re.compile(r"^model\.language_model\.layers\.(\d+)\.")
13
+ READOUT = {
14
+ "model.language_model.embed_tokens.weight",
15
+ "model.language_model.norm.weight",
16
+ "lm_head.weight",
17
+ }
18
+
19
+
20
+ def file_sha(path):
21
+ h = hashlib.sha256()
22
+ with Path(path).open("rb") as f:
23
+ for block in iter(lambda: f.read(8 * 1024 * 1024), b""):
24
+ h.update(block)
25
+ return h.hexdigest()
26
+
27
+
28
+ def source_snapshot(source):
29
+ """Stat all root model files; ignore unrelated nested evaluation directories."""
30
+ return {
31
+ p.name: {
32
+ "bytes": p.stat().st_size,
33
+ "mtime_ns": p.stat().st_mtime_ns,
34
+ "inode": p.stat().st_ino,
35
+ }
36
+ for p in sorted(Path(source).iterdir())
37
+ if p.is_file()
38
+ }
39
+
40
+
41
+ def assert_source_unchanged(source, before):
42
+ if source_snapshot(source) != before:
43
+ raise ValueError(
44
+ "source model files changed during export; refusing verified publication"
45
+ )
46
+
47
+
48
+ def verify_artifact(path, depth):
49
+ """Default launcher gate: rehash every weight, not only config and index."""
50
+ path = Path(path)
51
+ config = json.loads((path / "config.json").read_text())
52
+ text = config.get("text_config", {})
53
+ if (
54
+ config.get("model_type") != "qwen3_5"
55
+ or text.get("hidden_size") != 4096
56
+ or text.get("vocab_size") != 248320
57
+ or text.get("num_hidden_layers") != depth
58
+ or len(text.get("layer_types", [])) != depth
59
+ or (path / "adapter_config.json").exists()
60
+ ):
61
+ raise ValueError("backend architecture/depth mismatch")
62
+ if depth == 32:
63
+ check_source(path)
64
+ return
65
+ if depth != 16 or (path / "EXPORT_INCOMPLETE").exists():
66
+ raise ValueError("only complete 16/32 exports accepted")
67
+ manifest = json.loads((path / "export-low-provenance.json").read_text())
68
+ if (
69
+ manifest.get("status") != "verified"
70
+ or manifest.get("actual_depth") != 16
71
+ or manifest.get("all_retained_tensors_bit_identical") is not True
72
+ ):
73
+ raise ValueError("low export provenance is not verified")
74
+ for name, field in (
75
+ ("config.json", "export_config_sha256"),
76
+ ("model.safetensors.index.json", "export_index_sha256"),
77
+ ):
78
+ if file_sha(path / name) != manifest[field]:
79
+ raise ValueError("low config/index SHA mismatch")
80
+ weights = json.loads((path / "model.safetensors.index.json").read_text())[
81
+ "weight_map"
82
+ ]
83
+ select_keys(weights, 16, 16)
84
+ entries = manifest["export_weight_files"]
85
+ expected = {entry["file"]: entry for entry in entries}
86
+ if len(expected) != len(entries) or set(expected) != set(weights.values()):
87
+ raise ValueError("low weight manifest/index mismatch")
88
+ for name, entry in expected.items():
89
+ if Path(name).name != name or not name.endswith(".safetensors"):
90
+ raise ValueError("invalid low shard path")
91
+ if (path / name).stat().st_size != entry["bytes"] or file_sha(
92
+ path / name
93
+ ) != entry["sha256"]:
94
+ raise ValueError(f"low weight SHA mismatch: {name}")
95
+
96
+
97
+ def tensor_sha(tensor):
98
+ import torch
99
+
100
+ raw = memoryview(tensor.contiguous().view(torch.uint8).numpy()).cast("B")
101
+ h = hashlib.sha256()
102
+ for start in range(0, len(raw), 8 * 1024 * 1024):
103
+ h.update(raw[start : start + 8 * 1024 * 1024])
104
+ return h.hexdigest()
105
+
106
+
107
+ def prefix_config(config, depth=16):
108
+ result = copy.deepcopy(config)
109
+ text = result["text_config"]
110
+ if (
111
+ not 0 < depth < text["num_hidden_layers"]
112
+ or len(text["layer_types"]) != text["num_hidden_layers"]
113
+ ):
114
+ raise ValueError("invalid depth or layer_types")
115
+ text["num_hidden_layers"] = depth
116
+ text["layer_types"] = text["layer_types"][:depth]
117
+ if "mlp_only_layers" in text:
118
+ text["mlp_only_layers"] = [i for i in text["mlp_only_layers"] if i < depth]
119
+ return result
120
+
121
+
122
+ def select_keys(keys, depth, full_depth):
123
+ kept, removed, seen = [], [], set()
124
+ for key in sorted(keys):
125
+ match = LAYER.match(key)
126
+ if match:
127
+ i = int(match.group(1))
128
+ if not 0 <= i < full_depth:
129
+ raise ValueError("layer outside full depth")
130
+ seen.add(i)
131
+ (kept if i < depth else removed).append(key)
132
+ elif key in READOUT or key.startswith("model.visual."):
133
+ kept.append(key)
134
+ else:
135
+ raise ValueError(f"unexpected weight namespace: {key}")
136
+ if seen != set(range(full_depth)) or not READOUT <= set(kept):
137
+ raise ValueError("missing decoder, embedding, final norm or lm_head")
138
+ return kept, removed
139
+
140
+
141
+ def check_source(source):
142
+ config = json.loads((source / "config.json").read_text())
143
+ text = config.get("text_config", {})
144
+ if (
145
+ config.get("model_type") != "qwen3_5"
146
+ or config.get("architectures") != ["Qwen3_5ForConditionalGeneration"]
147
+ or text.get("num_hidden_layers") != 32
148
+ or text.get("hidden_size") != 4096
149
+ or text.get("vocab_size") != 248320
150
+ or text.get("intermediate_size") != 12288
151
+ or config.get("tie_word_embeddings") is not False
152
+ or text.get("tie_word_embeddings") is not False
153
+ or (source / "adapter_config.json").exists()
154
+ ):
155
+ raise ValueError("only merged, untied dense Qwen3.5-9B full32 accepted")
156
+ depth = json.loads((source / "depth_config.json").read_text())
157
+ if depth.get("exit_depth") != 16 or depth.get("full_depth") != 32:
158
+ raise ValueError("source must declare trained exits 16/32")
159
+ if text.get("layer_types") != (["linear_attention"] * 3 + ["full_attention"]) * 8:
160
+ raise ValueError("unexpected Qwen3.5-9B hybrid schedule")
161
+ weight_map = json.loads((source / "model.safetensors.index.json").read_text())[
162
+ "weight_map"
163
+ ]
164
+ kept, removed = select_keys(weight_map, 16, 32)
165
+ manifest = json.loads((source / "release-manifest.json").read_text())
166
+ expected = {r["path"]: r["sha256"] for r in manifest["files"]}
167
+ source_files = []
168
+ for name in sorted(set(weight_map.values())):
169
+ if Path(name).name != name or not name.endswith(".safetensors"):
170
+ raise ValueError("weight shard must be a simple safetensors filename")
171
+ observed = file_sha(source / name)
172
+ if expected.get(name) != observed:
173
+ raise ValueError(f"source weight SHA mismatch: {name}")
174
+ source_files.append(
175
+ {"file": name, "sha256": observed, "bytes": (source / name).stat().st_size}
176
+ )
177
+ for name in (
178
+ "config.json",
179
+ "depth_config.json",
180
+ "model.safetensors.index.json",
181
+ "tokenizer.json",
182
+ "tokenizer_config.json",
183
+ "chat_template.jinja",
184
+ ):
185
+ if name not in expected or file_sha(source / name) != expected[name]:
186
+ raise ValueError(f"source metadata SHA mismatch: {name}")
187
+ return config, weight_map, kept, removed, source_files
188
+
189
+
190
+ def copy_weights(source, output, weight_map, kept):
191
+ from safetensors import safe_open
192
+ from safetensors.torch import save_file
193
+
194
+ shards = [
195
+ n
196
+ for n in sorted(set(weight_map.values()))
197
+ if any(weight_map[k] == n for k in kept)
198
+ ]
199
+ output_map, files, manifests = {}, [], []
200
+ total = 0
201
+ for ordinal, name in enumerate(shards, 1):
202
+ selected = sorted(k for k in kept if weight_map[k] == name)
203
+ with safe_open(source / name, framework="pt", device="cpu") as src:
204
+ if set(src.keys()) != {k for k, v in weight_map.items() if v == name}:
205
+ raise ValueError("source shard/index mismatch")
206
+ tensors = {k: src.get_tensor(k) for k in selected}
207
+ total += sum(t.numel() * t.element_size() for t in tensors.values())
208
+ out_name = f"model-low16-{ordinal:05d}-of-{len(shards):05d}.safetensors"
209
+ save_file(tensors, output / out_name, metadata={"format": "pt"})
210
+ with safe_open(output / out_name, framework="pt", device="cpu") as dst:
211
+ if set(dst.keys()) != set(selected):
212
+ raise ValueError("exported keys mismatch")
213
+ for key in selected:
214
+ original, exported = tensors[key], dst.get_tensor(key)
215
+ checksum = tensor_sha(original)
216
+ if (
217
+ original.dtype != exported.dtype
218
+ or original.shape != exported.shape
219
+ or checksum != tensor_sha(exported)
220
+ ):
221
+ raise ValueError(f"exported tensor changed: {key}")
222
+ manifests.append(
223
+ {
224
+ "name": key,
225
+ "shape": list(original.shape),
226
+ "dtype": str(original.dtype),
227
+ "sha256": checksum,
228
+ }
229
+ )
230
+ output_map[key] = out_name
231
+ files.append(
232
+ {
233
+ "file": out_name,
234
+ "sha256": file_sha(output / out_name),
235
+ "bytes": (output / out_name).stat().st_size,
236
+ }
237
+ )
238
+ print(
239
+ json.dumps(
240
+ {"verified_export_shard": out_name, "tensor_count": len(selected)}
241
+ ),
242
+ flush=True,
243
+ )
244
+ if set(output_map) != set(kept):
245
+ raise ValueError("export index incomplete")
246
+ (output / "model.safetensors.index.json").write_text(
247
+ json.dumps(
248
+ {"metadata": {"total_size": total}, "weight_map": output_map}, indent=2
249
+ )
250
+ + "\n"
251
+ )
252
+ return files, manifests
253
+
254
+
255
+ def export(source, output):
256
+ from safetensors import safe_open
257
+
258
+ source, output = Path(source).resolve(), Path(output).resolve()
259
+ if output.exists() or source in output.parents:
260
+ raise ValueError("output must be a new directory outside source model")
261
+ before = source_snapshot(source)
262
+ config, weight_map, kept, removed, source_files = check_source(source)
263
+ for name in sorted(set(weight_map.values())):
264
+ with safe_open(source / name, framework="pt", device="cpu") as handle:
265
+ if set(handle.keys()) != {k for k, v in weight_map.items() if v == name}:
266
+ raise ValueError("source index/shard mismatch")
267
+ for key, shape in {
268
+ "lm_head.weight": (248320, 4096),
269
+ "model.language_model.embed_tokens.weight": (248320, 4096),
270
+ "model.language_model.norm.weight": (4096,),
271
+ }.items():
272
+ if (
273
+ weight_map[key] == name
274
+ and tuple(handle.get_slice(key).get_shape()) != shape
275
+ ):
276
+ raise ValueError(f"unexpected shared readout shape: {key}")
277
+ output.mkdir(parents=True, exist_ok=False)
278
+ (output / "EXPORT_INCOMPLETE").write_text(
279
+ "Only provenance status=verified denotes success.\n"
280
+ )
281
+ (output / "config.json").write_text(
282
+ json.dumps(prefix_config(config), indent=2) + "\n"
283
+ )
284
+ for name in (
285
+ "tokenizer.json",
286
+ "tokenizer_config.json",
287
+ "chat_template.jinja",
288
+ "generation_config.json",
289
+ "special_tokens_map.json",
290
+ "added_tokens.json",
291
+ "vocab.json",
292
+ "merges.txt",
293
+ "preprocessor_config.json",
294
+ "processor_config.json",
295
+ "video_preprocessor_config.json",
296
+ "LICENSE",
297
+ ):
298
+ if (source / name).is_file():
299
+ shutil.copy2(source / name, output / name)
300
+ files, tensors = copy_weights(source, output, weight_map, kept)
301
+ provenance = {
302
+ "format": "openjev.vllm-low-prefix.v1",
303
+ "status": "verified",
304
+ "created_utc": datetime.now(timezone.utc).isoformat(),
305
+ "source": str(source),
306
+ "source_full_depth": 32,
307
+ "actual_depth": 16,
308
+ "dynamic_continuation": False,
309
+ "selection": "original decoder 0..15 + shared embed/final norm/lm_head + all visual tensors",
310
+ "source_config_sha256": file_sha(source / "config.json"),
311
+ "source_depth_config": json.loads((source / "depth_config.json").read_text()),
312
+ "source_release_manifest_sha256": file_sha(source / "release-manifest.json"),
313
+ "source_index_sha256": file_sha(source / "model.safetensors.index.json"),
314
+ "source_weight_files": source_files,
315
+ "export_config_sha256": file_sha(output / "config.json"),
316
+ "export_index_sha256": file_sha(output / "model.safetensors.index.json"),
317
+ "export_weight_files": files,
318
+ "retained_tensors": tensors,
319
+ "removed_tensors": removed,
320
+ "all_retained_tensors_bit_identical": True,
321
+ "vllm_numerical_parity": "not established by CPU export; requires independent GPU comparison",
322
+ "script_sha256": file_sha(__file__),
323
+ }
324
+ assert_source_unchanged(source, before)
325
+ provenance["source_files_stat_before_after"] = before
326
+ provenance["source_files_unchanged_during_export"] = True
327
+ (output / "export-low-provenance.json").write_text(
328
+ json.dumps(provenance, indent=2) + "\n"
329
+ )
330
+ (output / "EXPORT_INCOMPLETE").unlink()
331
+ return provenance
332
+
333
+
334
+ def main():
335
+ p = argparse.ArgumentParser(description=__doc__)
336
+ p.add_argument("--source", type=Path, required=True)
337
+ p.add_argument("--output", type=Path, required=True)
338
+ args = p.parse_args()
339
+ result = export(args.source, args.output)
340
+ print(
341
+ json.dumps(
342
+ {
343
+ "status": result["status"],
344
+ "output": str(args.output),
345
+ "actual_depth": 16,
346
+ "retained_tensors": len(result["retained_tensors"]),
347
+ }
348
+ ),
349
+ flush=True,
350
+ )
351
+
352
+
353
+ if __name__ == "__main__":
354
+ main()
deployment/gateway.py ADDED
@@ -0,0 +1,430 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """TypeSafe SDK wire gateway to distinct shallow/full vLLM scoring engines."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import asyncio
7
+ from contextlib import asynccontextmanager
8
+ from dataclasses import dataclass
9
+ import hashlib
10
+ import hmac
11
+ import json
12
+ import math
13
+ import os
14
+ from pathlib import Path
15
+ import re
16
+ import uuid
17
+
18
+ import httpx
19
+ from fastapi import FastAPI, Request
20
+ from fastapi.responses import JSONResponse
21
+
22
+ from contracts import (
23
+ ServingError,
24
+ answer,
25
+ compile_question,
26
+ prepare_request,
27
+ probabilities,
28
+ )
29
+
30
+
31
+ def canonical_root(value):
32
+ url = httpx.URL(value)
33
+ path = url.path.rstrip("/").removesuffix("/v1").rstrip("/")
34
+ return str(url.copy_with(path=path))
35
+
36
+
37
+ @dataclass(frozen=True)
38
+ class Settings:
39
+ high_url: str
40
+ low_url: str
41
+ high_model: str
42
+ low_model: str
43
+ api_key: str
44
+ model_name: str = "APUS-OpenJev-v1-9B"
45
+ max_body_bytes: int = 1_048_576
46
+ max_questions: int = 16
47
+ max_tokens: int = 8191
48
+ max_inflight: int = 32
49
+ timeout: float = 120.0
50
+ high_model_path: str | None = None
51
+ low_model_path: str | None = None
52
+
53
+ def __post_init__(self):
54
+ for value in (self.high_url, self.low_url):
55
+ url = httpx.URL(value)
56
+ if (
57
+ url.scheme not in ("http", "https")
58
+ or not url.host
59
+ or url.username
60
+ or url.password
61
+ or url.query
62
+ or url.fragment
63
+ ):
64
+ raise ValueError(
65
+ "Backend URLs must be HTTP(S) without credentials/query"
66
+ )
67
+ if canonical_root(self.high_url) == canonical_root(self.low_url):
68
+ raise ValueError(
69
+ "Low and high must use distinct engine URLs; no silent full-depth fallback"
70
+ )
71
+ if not all((self.high_model, self.low_model, self.api_key, self.model_name)):
72
+ raise ValueError(
73
+ "Both model aliases and a nonempty local API key are required"
74
+ )
75
+ if self.high_model == self.low_model:
76
+ raise ValueError("Low and high engine model aliases must be distinct")
77
+
78
+ @classmethod
79
+ def from_env(cls):
80
+ return cls(
81
+ high_url=os.environ["JEV_HIGH_URL"],
82
+ low_url=os.environ["JEV_LOW_URL"],
83
+ high_model=os.environ["JEV_HIGH_MODEL"],
84
+ low_model=os.environ["JEV_LOW_MODEL"],
85
+ api_key=os.environ["JEV_API_KEY"],
86
+ high_model_path=os.environ["JEV_HIGH_MODEL_PATH"],
87
+ low_model_path=os.environ["JEV_LOW_MODEL_PATH"],
88
+ model_name=os.environ.get("JEV_MODEL_NAME", "APUS-OpenJev-v1-9B"),
89
+ )
90
+
91
+
92
+ def unique_object(pairs):
93
+ result = {}
94
+ for key, value in pairs:
95
+ if key in result:
96
+ raise ValueError("Duplicate JSON keys")
97
+ result[key] = value
98
+ return result
99
+
100
+
101
+ def reject_constant(value):
102
+ raise ValueError("Nonfinite JSON values are forbidden")
103
+
104
+
105
+ def parse_scores(data, tokens):
106
+ try:
107
+ choices = data["choices"]
108
+ if len(choices) != 1 or len(choices[0]["logprobs"]["top_logprobs"]) != 1:
109
+ raise ValueError
110
+ values = choices[0]["logprobs"]["top_logprobs"][0]
111
+ if not isinstance(values, dict):
112
+ raise ValueError
113
+ by_id = {}
114
+ for key, value in values.items():
115
+ if (
116
+ not re.fullmatch(r"token_id:(0|[1-9]\d*)", key)
117
+ or type(value) not in (float, int)
118
+ or not math.isfinite(value)
119
+ or value > 0
120
+ ):
121
+ raise ValueError
122
+ by_id[int(key.split(":")[1])] = value
123
+ return [by_id[token] for token in tokens]
124
+ except (KeyError, TypeError, ValueError, IndexError):
125
+ raise ServingError(
126
+ 502,
127
+ "invalid_backend_scores",
128
+ "Backend omitted valid candidate token logprobs",
129
+ ) from None
130
+
131
+
132
+ def endpoint(url, path):
133
+ root = url.rstrip("/")
134
+ return root + (path.removeprefix("/v1") if root.endswith("/v1") else path)
135
+
136
+
137
+ def file_sha(path):
138
+ return hashlib.sha256(Path(path).read_bytes()).hexdigest()
139
+
140
+
141
+ def verify_artifacts(settings):
142
+ """Bind configured backend roots to real model configs and export identity."""
143
+ if not settings.high_model_path or not settings.low_model_path:
144
+ raise ValueError("Both JEV_HIGH_MODEL_PATH and JEV_LOW_MODEL_PATH are required")
145
+ roots = {
146
+ "high": Path(settings.high_model_path).resolve(),
147
+ "low": Path(settings.low_model_path).resolve(),
148
+ }
149
+ for effort, depth in (("high", 32), ("low", 16)):
150
+ root = roots[effort]
151
+ cfg = json.loads((root / "config.json").read_text())
152
+ text = cfg.get("text_config", cfg)
153
+ if (
154
+ cfg.get("model_type") not in ("qwen3_5", "qwen3_5_text")
155
+ or text.get("num_hidden_layers") != depth
156
+ or len(text.get("layer_types", [])) != depth
157
+ or text.get("hidden_size") != 4096
158
+ ):
159
+ raise ValueError(
160
+ "Backend artifact does not declare the required execution depth"
161
+ )
162
+ if (root / "adapter_config.json").exists() or (
163
+ root / "EXPORT_INCOMPLETE"
164
+ ).exists():
165
+ raise ValueError("Backend artifact is not a completed merged model")
166
+ if not (root / "model.safetensors.index.json").is_file():
167
+ raise ValueError("Backend artifact has no merged weight index")
168
+ low, high = roots["low"], roots["high"]
169
+ proof = json.loads((low / "export-low-provenance.json").read_text())
170
+ if (
171
+ proof.get("status") != "verified"
172
+ or proof.get("actual_depth") != 16
173
+ or proof.get("all_retained_tensors_bit_identical") is not True
174
+ ):
175
+ raise ValueError("Low artifact has not passed prefix export verification")
176
+ for key, path in {
177
+ "export_config_sha256": low / "config.json",
178
+ "export_index_sha256": low / "model.safetensors.index.json",
179
+ "source_config_sha256": high / "config.json",
180
+ "source_index_sha256": high / "model.safetensors.index.json",
181
+ "source_release_manifest_sha256": high / "release-manifest.json",
182
+ }.items():
183
+ if proof.get(key) != file_sha(path):
184
+ raise ValueError(
185
+ "Low export provenance does not match the configured source or export metadata"
186
+ )
187
+ return roots
188
+
189
+
190
+ def create_app(settings, tokenizer, transport=None, check_backends=True):
191
+ @asynccontextmanager
192
+ async def lifespan(app):
193
+ async with httpx.AsyncClient(
194
+ timeout=settings.timeout,
195
+ trust_env=False,
196
+ follow_redirects=False,
197
+ transport=transport,
198
+ ) as client:
199
+ app.state.client = client
200
+ if check_backends:
201
+ roots = verify_artifacts(settings)
202
+ for effort, url, alias in (
203
+ ("low", settings.low_url, settings.low_model),
204
+ ("high", settings.high_url, settings.high_model),
205
+ ):
206
+ response = await client.get(endpoint(url, "/v1/models"))
207
+ response.raise_for_status()
208
+ matched = [m for m in response.json()["data"] if m["id"] == alias]
209
+ if (
210
+ len(matched) != 1
211
+ or not matched[0].get("root")
212
+ or Path(matched[0]["root"]).resolve() != roots[effort]
213
+ ):
214
+ raise RuntimeError(
215
+ "Engine alias/root does not match the verified depth artifact"
216
+ )
217
+ app.state.ready = True
218
+ yield
219
+ app.state.ready = False
220
+
221
+ app = FastAPI(lifespan=lifespan)
222
+ app.state.ready = False
223
+ app.state.inflight = 0
224
+
225
+ def authorize(request):
226
+ authorization = request.headers.get("authorization", "")
227
+ if not hmac.compare_digest(
228
+ authorization.encode(), ("Bearer " + settings.api_key).encode()
229
+ ):
230
+ raise ServingError(401, "unauthorized", "Invalid local API key")
231
+
232
+ @app.middleware("http")
233
+ async def request_id_header(request, call_next):
234
+ response = await call_next(request)
235
+ response.headers.setdefault("x-typesafe-request-id", str(uuid.uuid4()))
236
+ return response
237
+
238
+ @app.exception_handler(ServingError)
239
+ async def serving_error(_request, exc):
240
+ return JSONResponse(
241
+ status_code=exc.status,
242
+ content={"error": {"code": exc.code, "message": exc.message}},
243
+ )
244
+
245
+ @app.get("/health")
246
+ async def health():
247
+ return JSONResponse(
248
+ status_code=200 if app.state.ready else 503,
249
+ content={
250
+ "ready": app.state.ready,
251
+ "model": settings.model_name,
252
+ "efforts": {"low": 16, "high": 32},
253
+ "routing": "separate-vllm-engines",
254
+ "automatic_routing": False,
255
+ },
256
+ )
257
+
258
+ @app.get("/v1/models")
259
+ async def models(request: Request):
260
+ authorize(request)
261
+ return {
262
+ "models": [
263
+ {"name": name, "description": description, "release_date": "2026-09-21"}
264
+ for name, description in [
265
+ (
266
+ settings.model_name,
267
+ "APUS local decision model; explicit low16/high32; uncalibrated candidate scores.",
268
+ ),
269
+ (
270
+ "jev-latest",
271
+ "Compatibility alias for "
272
+ + settings.model_name
273
+ + "; NOT the official Jev model.",
274
+ ),
275
+ ]
276
+ ]
277
+ }
278
+
279
+ @app.post("/v1/systemone")
280
+ async def systemone(request: Request):
281
+ authorize(request)
282
+ if not app.state.ready:
283
+ raise ServingError(503, "not_ready", "Backends are not ready")
284
+ if app.state.inflight >= settings.max_inflight:
285
+ raise ServingError(429, "busy", "Gateway in-flight budget exceeded")
286
+ app.state.inflight += 1
287
+ try:
288
+ async with asyncio.timeout(settings.timeout):
289
+ raw = bytearray()
290
+ async for chunk in request.stream():
291
+ raw.extend(chunk)
292
+ if len(raw) > settings.max_body_bytes:
293
+ raise ServingError(
294
+ 413, "request_too_large", "Request byte budget exceeded"
295
+ )
296
+ try:
297
+ body = json.loads(
298
+ raw,
299
+ object_pairs_hook=unique_object,
300
+ parse_constant=reject_constant,
301
+ )
302
+ if not isinstance(body, dict):
303
+ raise ValueError
304
+ except (ValueError, TypeError, RecursionError, UnicodeError):
305
+ raise ServingError(
306
+ 422, "invalid_json", "Malformed JSON object"
307
+ ) from None
308
+ effort = body.get("effort", "high")
309
+ if effort not in ("low", "high"):
310
+ raise ServingError(
311
+ 422, "invalid_effort", "effort must be low or high"
312
+ )
313
+ questions = prepare_request(
314
+ body,
315
+ model_name=settings.model_name,
316
+ max_questions=settings.max_questions,
317
+ allow_score=True,
318
+ )
319
+ compiled = [
320
+ compile_question(q, tokenizer, max_tokens=settings.max_tokens)
321
+ for q in questions
322
+ ]
323
+ url, model, depth = (
324
+ (settings.low_url, settings.low_model, 16)
325
+ if effort == "low"
326
+ else (settings.high_url, settings.high_model, 32)
327
+ )
328
+ answers = {}
329
+ for item in compiled:
330
+ if not item.prompt_ids:
331
+ answers[item.question.key] = answer(item.question, [1.0])
332
+ continue
333
+ payload = {
334
+ "model": model,
335
+ "prompt": item.prompt_ids,
336
+ "max_tokens": 1,
337
+ "n": 1,
338
+ "stream": False,
339
+ "echo": False,
340
+ "add_special_tokens": False,
341
+ "logprobs": len(item.candidate_token_ids),
342
+ "logprob_token_ids": item.candidate_token_ids,
343
+ "return_tokens_as_token_ids": True,
344
+ "temperature": 1.0,
345
+ "top_p": 1.0,
346
+ "top_k": -1,
347
+ "min_p": 0.0,
348
+ "frequency_penalty": 0.0,
349
+ "presence_penalty": 0.0,
350
+ "repetition_penalty": 1.0,
351
+ }
352
+ response = await app.state.client.post(
353
+ endpoint(url, "/v1/completions"), json=payload
354
+ )
355
+ if response.status_code != 200:
356
+ raise ServingError(
357
+ 502,
358
+ "backend_error",
359
+ f"Inference backend returned HTTP {response.status_code}",
360
+ )
361
+ try:
362
+ data = json.loads(
363
+ response.content,
364
+ object_pairs_hook=unique_object,
365
+ parse_constant=reject_constant,
366
+ )
367
+ except (ValueError, TypeError, UnicodeError, RecursionError):
368
+ raise ServingError(
369
+ 502, "backend_json", "Backend returned invalid JSON"
370
+ ) from None
371
+ answers[item.question.key] = answer(
372
+ item.question,
373
+ probabilities(parse_scores(data, item.candidate_token_ids)),
374
+ )
375
+ result = {
376
+ "model": settings.model_name,
377
+ "answers": answers,
378
+ "usage": {
379
+ "input_tokens": sum(len(c.prompt_ids) for c in compiled),
380
+ "output_tokens": sum(bool(c.prompt_ids) for c in compiled),
381
+ "billing_units": len(compiled),
382
+ },
383
+ }
384
+ return JSONResponse(
385
+ result,
386
+ headers={
387
+ "x-openjev-effort": effort,
388
+ "x-openjev-executed-layers": str(
389
+ depth if any(c.prompt_ids for c in compiled) else 0
390
+ ),
391
+ "x-openjev-engine-model": model,
392
+ "x-typesafe-request-id": str(uuid.uuid4()),
393
+ },
394
+ )
395
+ except (TimeoutError, httpx.TimeoutException):
396
+ raise ServingError(
397
+ 504, "deadline_exceeded", "Inference request exceeded timeout"
398
+ ) from None
399
+ except httpx.HTTPError:
400
+ raise ServingError(
401
+ 502, "backend_transport", "Inference backend connection failed"
402
+ ) from None
403
+ finally:
404
+ app.state.inflight -= 1
405
+
406
+ return app
407
+
408
+
409
+ def main():
410
+ parser = argparse.ArgumentParser(description=__doc__)
411
+ parser.add_argument("--host", default="127.0.0.1")
412
+ parser.add_argument("--port", type=int, default=30013)
413
+ args = parser.parse_args()
414
+ from transformers import AutoTokenizer
415
+ import uvicorn
416
+
417
+ settings = Settings.from_env()
418
+ if (
419
+ Path(os.environ["JEV_MODEL_PATH"]).resolve()
420
+ != Path(settings.high_model_path).resolve()
421
+ ):
422
+ raise ValueError("Tokenizer must come from the configured full-depth model")
423
+ tokenizer = AutoTokenizer.from_pretrained(
424
+ os.environ["JEV_MODEL_PATH"], local_files_only=True
425
+ )
426
+ uvicorn.run(create_app(settings, tokenizer), host=args.host, port=args.port)
427
+
428
+
429
+ if __name__ == "__main__":
430
+ main()
deployment/requirements-test.txt ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # CPU SDK/gateway tests can run without installing vLLM or PyTorch.
2
+ fastapi==0.128.8
3
+ uvicorn==0.40.0
4
+ httpx==0.28.1
5
+ httpx2==2.13.0
6
+ typesafe-sdk==0.7.0
7
+ pytest==9.0.2
deployment/requirements.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # vLLM owns the CUDA PyTorch dependency; use a compatible CUDA 13 compiler on Blackwell.
2
+ vllm==0.29.0
3
+ transformers==5.17.0
4
+ huggingface-hub==1.32.0
5
+ ninja==1.13.2
6
+ fastapi==0.128.8
7
+ uvicorn==0.40.0
8
+ httpx==0.28.1
9
+ httpx2==2.13.0
10
+ typesafe-sdk==0.7.0
deployment/run_backend.sh ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # Launch one dedicated depth backend; never stops another process.
3
+ set -euo pipefail
4
+ BUNDLE=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
5
+ : "${JEV_VLLM_PYTHON:?Absolute Python path in the vLLM 0.29.0 venv}"
6
+ : "${JEV_MODEL_PATH:?Path to full32 merged model or verified exported low16}"
7
+ : "${CUDA_VISIBLE_DEVICES:?Choose exactly one GPU UUID or index}"
8
+ JEV_EFFORT=${JEV_EFFORT:-low}
9
+ case "$JEV_EFFORT" in
10
+ low) DEPTH=16; DEFAULT_PORT=30004; DEFAULT_ALIAS=apus-openjev-9b-low16; DEFAULT_MEMORY=0.22 ;;
11
+ high) DEPTH=32; DEFAULT_PORT=30002; DEFAULT_ALIAS=apus-openjev-9b-high32; DEFAULT_MEMORY=0.7 ;;
12
+ *) echo 'JEV_EFFORT must be high or low' >&2; exit 1 ;;
13
+ esac
14
+ JEV_ENGINE_PORT=${JEV_ENGINE_PORT:-$DEFAULT_PORT}
15
+ JEV_MODEL_ALIAS=${JEV_MODEL_ALIAS:-$DEFAULT_ALIAS}
16
+ JEV_GPU_MEMORY_UTILIZATION=${JEV_GPU_MEMORY_UTILIZATION:-$DEFAULT_MEMORY}
17
+ JEV_MAX_MODEL_LEN=${JEV_MAX_MODEL_LEN:-8192}
18
+ JEV_MAX_NUM_SEQS=${JEV_MAX_NUM_SEQS:-8}
19
+ [[ "$JEV_VLLM_PYTHON" = /* && -x "$JEV_VLLM_PYTHON" ]] || exit 1
20
+ [[ "$CUDA_VISIBLE_DEVICES" =~ ^([0-9]+|GPU-[a-fA-F0-9-]+)$ ]] || exit 1
21
+ export PATH="$(dirname -- "$JEV_VLLM_PYTHON"):$PATH"
22
+ if [[ -n "${CUDA_HOME:-}" ]]; then
23
+ [[ -x "$CUDA_HOME/bin/nvcc" ]] || { echo 'Invalid CUDA_HOME/bin/nvcc' >&2; exit 1; }
24
+ export PATH="$CUDA_HOME/bin:$PATH"
25
+ fi
26
+ # This checks the target port and artifact only. Co-residence with high is intentional.
27
+ "$JEV_VLLM_PYTHON" - "$JEV_MODEL_PATH" "$DEPTH" "$JEV_ENGINE_PORT" "$BUNDLE" <<'PY'
28
+ import importlib.metadata,json,socket,sys
29
+ from pathlib import Path
30
+ p=Path(sys.argv[1]); depth=int(sys.argv[2]); port=int(sys.argv[3])
31
+ assert importlib.metadata.version('vllm')=='0.29.0', 'vLLM must be 0.29.0'
32
+ sys.path.insert(0,sys.argv[4])
33
+ from export_low import verify_artifact
34
+ verify_artifact(p,depth)
35
+ with socket.socket() as s:
36
+ s.bind(('127.0.0.1',port))
37
+ print(json.dumps({'model':str(p),'actual_depth':depth,'port':port,'dynamic_continuation':False}),flush=True)
38
+ PY
39
+ export HF_HUB_OFFLINE=1 TRANSFORMERS_OFFLINE=1 TOKENIZERS_PARALLELISM=false
40
+ export VLLM_USE_FLASHINFER_SAMPLER=0
41
+ export OMP_NUM_THREADS=${OMP_NUM_THREADS:-8}
42
+ exec "$JEV_VLLM_PYTHON" "$(dirname -- "$JEV_VLLM_PYTHON")/vllm" serve "$JEV_MODEL_PATH" \
43
+ --served-model-name "$JEV_MODEL_ALIAS" --host 127.0.0.1 --port "$JEV_ENGINE_PORT" \
44
+ --dtype bfloat16 --tensor-parallel-size 1 --max-model-len "$JEV_MAX_MODEL_LEN" \
45
+ --language-model-only --gpu-memory-utilization "$JEV_GPU_MEMORY_UTILIZATION" \
46
+ --max-num-seqs "$JEV_MAX_NUM_SEQS" --max-num-batched-tokens "$JEV_MAX_MODEL_LEN" \
47
+ --max-logprobs 256 --logprobs-mode raw_logprobs --generation-config vllm \
48
+ --no-enable-prefix-caching
deployment/sdk_example.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """English customer-support examples using the official TypeSafe Python SDK."""
2
+
3
+ import argparse
4
+ import os
5
+
6
+ from typesafe_sdk import Choice, Noul, RetryPolicy, Score, TypeSafeClient
7
+
8
+
9
+ def main():
10
+ parser = argparse.ArgumentParser(description=__doc__)
11
+ parser.add_argument("--base-url", default="http://127.0.0.1:30013")
12
+ parser.add_argument("--model", default="APUS-OpenJev-v1-9B")
13
+ parser.add_argument("--effort", choices=["low", "high"], default="high")
14
+ args = parser.parse_args()
15
+ with TypeSafeClient(
16
+ base_url=args.base_url,
17
+ api_key=os.environ["JEV_API_KEY"],
18
+ model=args.model,
19
+ retry=RetryPolicy(max_retries=0),
20
+ ) as client:
21
+ print(client.models.list().model_dump_json(indent=2))
22
+ result = client.system_one(
23
+ state={
24
+ "customer_message": "My order arrived damaged yesterday. I would like a refund.",
25
+ "policy": "A damaged item reported within 30 days is eligible for a refund.",
26
+ },
27
+ questions={
28
+ "support_queue": Choice(
29
+ instructions="Choose the team that should handle this ticket.",
30
+ criteria={
31
+ "returns": "Damaged items, returns, and refunds",
32
+ "billing": "Invoice or payment issues",
33
+ "shipping": "Tracking and delivery updates",
34
+ },
35
+ ),
36
+ "refund_eligible": Noul(
37
+ instructions="Does the stated policy make this customer eligible for a refund?"
38
+ ),
39
+ "urgency": Score(
40
+ instructions="Rate the urgency of this support ticket.",
41
+ criteria=[
42
+ "Routine question",
43
+ "Customer needs timely assistance",
44
+ "Immediate safety or security risk",
45
+ ],
46
+ ),
47
+ },
48
+ extra_body={"effort": args.effort},
49
+ )
50
+ print(result.model_dump_json(indent=2))
51
+ print("Effort:", result.raw_http_response.headers.get("x-openjev-effort"))
52
+ print(
53
+ "Configured execution layers:",
54
+ result.raw_http_response.headers.get("x-openjev-executed-layers"),
55
+ )
56
+
57
+
58
+ if __name__ == "__main__":
59
+ main()
deployment/smoke_sdk.py ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Live official-SDK protocol smoke; writes requests/responses, never auth headers."""
2
+
3
+ import argparse
4
+ import asyncio
5
+ from datetime import datetime, timezone
6
+ import importlib.metadata
7
+ import json
8
+ import os
9
+ from pathlib import Path
10
+ import time
11
+
12
+ from typesafe_sdk import (
13
+ AsyncTypeSafeClient,
14
+ Choice,
15
+ Noul,
16
+ RetryPolicy,
17
+ Score,
18
+ TypeSafeClient,
19
+ TypeSafeAPIError,
20
+ )
21
+
22
+
23
+ def now():
24
+ return datetime.now(timezone.utc).isoformat()
25
+
26
+
27
+ def main():
28
+ parser = argparse.ArgumentParser(description=__doc__)
29
+ parser.add_argument("--base-url", default="http://127.0.0.1:30013")
30
+ parser.add_argument("--model", default="APUS-OpenJev-v1-9B")
31
+ parser.add_argument("--output", type=Path, required=True)
32
+ args = parser.parse_args()
33
+ args.output.mkdir(parents=True, exist_ok=False)
34
+ common = {
35
+ "base_url": args.base_url,
36
+ "api_key": os.environ["JEV_API_KEY"],
37
+ "model": args.model,
38
+ "retry": RetryPolicy(max_retries=0),
39
+ "timeout": 120,
40
+ }
41
+ rows = []
42
+
43
+ def record(name, started, timer, response, request):
44
+ raw = response.raw_http_response
45
+ row = {
46
+ "case": name,
47
+ "started_at": started,
48
+ "finished_at": now(),
49
+ "elapsed_ms": (time.perf_counter() - timer) * 1000,
50
+ "request": json.loads(raw.request.content)
51
+ if raw.request.content
52
+ else request,
53
+ "status_code": raw.status_code,
54
+ "response_text": raw.text,
55
+ "response_headers": {
56
+ k: v
57
+ for k, v in raw.headers.items()
58
+ if k.lower()
59
+ in (
60
+ "x-openjev-effort",
61
+ "x-openjev-executed-layers",
62
+ "x-openjev-engine-model",
63
+ "x-typesafe-request-id",
64
+ "content-type",
65
+ )
66
+ },
67
+ }
68
+ rows.append(row)
69
+ with (args.output / "requests.jsonl").open("a") as out:
70
+ out.write(json.dumps(row, ensure_ascii=False) + "\n")
71
+
72
+ state = {
73
+ "message": "My order arrived damaged yesterday. I would like a refund.",
74
+ "policy": "Damaged goods reported within 30 days qualify for a refund.",
75
+ }
76
+ questions = {
77
+ "queue": Choice(
78
+ instructions="Choose the support team.",
79
+ criteria={"returns": "Damaged items and refunds", "billing": None},
80
+ ),
81
+ "eligible": Noul(
82
+ instructions="Is this customer eligible under the stated policy?"
83
+ ),
84
+ "urgency": Score(
85
+ instructions="Rate support urgency.",
86
+ criteria=[
87
+ "Routine",
88
+ {"meaning": "Timely attention required"},
89
+ ["Immediate safety risk"],
90
+ ],
91
+ ),
92
+ }
93
+ with TypeSafeClient(**common) as sdk:
94
+ t = time.perf_counter()
95
+ started = now()
96
+ result = sdk.models.list()
97
+ record("models", started, t, result, {"method": "GET", "path": "/v1/models"})
98
+ assert any(m.name == args.model for m in result.models)
99
+ for effort, depth in [("high", "32"), ("low", "16")]:
100
+ t = time.perf_counter()
101
+ started = now()
102
+ result = sdk.system_one(
103
+ state=state, questions=questions, extra_body={"effort": effort}
104
+ )
105
+ record(
106
+ "multi-" + effort,
107
+ started,
108
+ t,
109
+ result,
110
+ {
111
+ "state": state,
112
+ "questions": questions,
113
+ "model": args.model,
114
+ "effort": effort,
115
+ },
116
+ )
117
+ assert (
118
+ result.raw_http_response.headers["x-openjev-executed-layers"] == depth
119
+ )
120
+ assert set(result.answers) == set(questions)
121
+ assert result.answers["queue"].choice in ("returns", "billing")
122
+ assert 0 <= result.answers["eligible"].noul <= 1
123
+ assert result.answers["urgency"].legend[1] == {
124
+ "meaning": "Timely attention required"
125
+ }
126
+ assert (
127
+ abs(
128
+ result.answers["urgency"].score
129
+ - sum(
130
+ k * v
131
+ for k, v in result.answers["urgency"].probabilities.items()
132
+ )
133
+ )
134
+ < 1e-6
135
+ )
136
+ t = time.perf_counter()
137
+ started = now()
138
+ try:
139
+ sdk.system_one(
140
+ state="Support ticket.",
141
+ questions={"q": Noul()},
142
+ extra_body={"effort": "invalid"},
143
+ )
144
+ except TypeSafeAPIError as exc:
145
+ status = exc.status
146
+ error_body = exc.body
147
+ row = {
148
+ "case": "invalid-effort",
149
+ "started_at": started,
150
+ "finished_at": now(),
151
+ "elapsed_ms": (time.perf_counter() - t) * 1000,
152
+ "request": {
153
+ "state": "Support ticket.",
154
+ "questions": {"q": {"type": "noul"}},
155
+ "effort": "invalid",
156
+ },
157
+ "status_code": status,
158
+ "response_text": json.dumps(error_body),
159
+ }
160
+ rows.append(row)
161
+ with (args.output / "requests.jsonl").open("a") as out:
162
+ out.write(json.dumps(row) + "\n")
163
+ assert status == 422
164
+ else:
165
+ raise AssertionError("Invalid effort was silently accepted")
166
+
167
+ async def run_async():
168
+ async with AsyncTypeSafeClient(**common) as sdk:
169
+ t = time.perf_counter()
170
+ started = now()
171
+ result = await sdk.system_one(
172
+ state="The customer requests a refund.",
173
+ questions={"refund": Noul(instructions="Is this a refund request?")},
174
+ extra_body={"effort": "low"},
175
+ )
176
+ record(
177
+ "async-low",
178
+ started,
179
+ t,
180
+ result,
181
+ {
182
+ "state": "The customer requests a refund.",
183
+ "questions": {
184
+ "refund": {
185
+ "type": "noul",
186
+ "instructions": "Is this a refund request?",
187
+ }
188
+ },
189
+ "effort": "low",
190
+ },
191
+ )
192
+ assert result.raw_http_response.headers["x-openjev-executed-layers"] == "16"
193
+
194
+ asyncio.run(run_async())
195
+ summary = {
196
+ "status": "passed",
197
+ "sdk_version": importlib.metadata.version("typesafe-sdk"),
198
+ "base_url": args.base_url,
199
+ "model": args.model,
200
+ "completed_at": now(),
201
+ "cases": len(rows),
202
+ "scope": "SDK wire/routing smoke, not model-quality or performance acceptance",
203
+ }
204
+ (args.output / "summary.json").write_text(json.dumps(summary, indent=2) + "\n")
205
+ print(json.dumps(summary))
206
+
207
+
208
+ if __name__ == "__main__":
209
+ main()
deployment/tests/test_typesafe_gateway.py ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Official SDK 0.7.0 through real ASGI gateway, with deterministic backend."""
2
+
3
+ import asyncio
4
+ import json
5
+ from pathlib import Path
6
+ import sys
7
+
8
+ import httpx
9
+ import httpx2
10
+ import pytest
11
+ from fastapi.testclient import TestClient
12
+ from typesafe_sdk import (
13
+ AsyncTypeSafeClient,
14
+ Choice,
15
+ Noul,
16
+ Score,
17
+ TypeSafeClient,
18
+ RetryPolicy,
19
+ )
20
+
21
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
22
+ from gateway import Settings, create_app # noqa: E402
23
+ from contracts import BINARY_CRITERIA, prepare_request, compile_question # noqa: E402
24
+
25
+
26
+ class Tokenizer:
27
+ all_special_ids = [0]
28
+
29
+ def apply_chat_template(self, messages, **kwargs):
30
+ assert kwargs["enable_thinking"] is False
31
+ return "<chat>" + messages[0]["content"] + "</chat>"
32
+
33
+ def encode(self, text, **kwargs):
34
+ return [ord(c) + 1 for c in text]
35
+
36
+
37
+ @pytest.fixture
38
+ def service():
39
+ calls = []
40
+
41
+ def upstream(request):
42
+ payload = json.loads(request.content)
43
+ calls.append((request.url.host, payload))
44
+ tokens = payload["logprob_token_ids"]
45
+ scores = {f"token_id:{t}": -(i + 1.0) for i, t in enumerate(tokens)}
46
+ return httpx.Response(
47
+ 200, json={"choices": [{"logprobs": {"top_logprobs": [scores]}}]}
48
+ )
49
+
50
+ settings = Settings(
51
+ "http://high:8000", "http://low:8000", "apus-high32", "apus-low16", "test-key"
52
+ )
53
+ with TestClient(
54
+ create_app(
55
+ settings,
56
+ Tokenizer(),
57
+ transport=httpx.MockTransport(upstream),
58
+ check_backends=False,
59
+ )
60
+ ) as web:
61
+ yield web, calls
62
+
63
+
64
+ def sdk_transport(web):
65
+ def handler(req):
66
+ result = web.request(
67
+ req.method,
68
+ req.url.raw_path.decode(),
69
+ content=req.content,
70
+ headers=dict(req.headers),
71
+ )
72
+ return httpx2.Response(
73
+ result.status_code, content=result.content, headers=dict(result.headers)
74
+ )
75
+
76
+ return httpx2.MockTransport(handler)
77
+
78
+
79
+ def client(web, **kwargs):
80
+ return TypeSafeClient(
81
+ base_url="http://local",
82
+ api_key="test-key",
83
+ retry=RetryPolicy(max_retries=0),
84
+ transport=sdk_transport(web),
85
+ **kwargs,
86
+ )
87
+
88
+
89
+ def test_sync_sdk_all_primitives_and_models(service):
90
+ web, calls = service
91
+ with client(web) as sdk:
92
+ listed = sdk.models.list()
93
+ assert listed.models[0].name == "APUS-OpenJev-v1-9B"
94
+ assert "NOT the official" in listed.models[1].description
95
+ response = sdk.system_one(
96
+ state={"ticket": "My delivery is late."},
97
+ questions={
98
+ "queue": Choice(
99
+ instructions="Select the support queue.",
100
+ criteria={"shipping": "Delivery issues", "billing": None},
101
+ ),
102
+ "refund": Noul(
103
+ instructions=["Decide eligibility"],
104
+ criteria={"true": {"rule": "eligible"}, "false": ["ineligible"]},
105
+ ),
106
+ "urgency": Score(
107
+ instructions="Rate urgency.",
108
+ criteria=[
109
+ "Can wait",
110
+ {"description": "Time-sensitive"},
111
+ ["Immediate"],
112
+ ],
113
+ ),
114
+ },
115
+ extra_body={"effort": "low"},
116
+ )
117
+ assert response.answers["queue"].choice == "shipping"
118
+ assert 0 < response.answers["refund"].noul < 1
119
+ assert response.answers["urgency"].legend[1] == {"description": "Time-sensitive"}
120
+ assert response.answers["urgency"].score == pytest.approx(
121
+ sum(k * v for k, v in response.answers["urgency"].probabilities.items())
122
+ )
123
+ assert response.raw_http_response.headers["x-openjev-executed-layers"] == "16"
124
+ assert len(calls) == 3 and all(host == "low" for host, _ in calls)
125
+ assert all(p["model"] == "apus-low16" and p["max_tokens"] == 1 for _, p in calls)
126
+
127
+
128
+ def test_async_sdk_default_high(service):
129
+ web, calls = service
130
+
131
+ async def run():
132
+ async with AsyncTypeSafeClient(
133
+ base_url="http://local",
134
+ api_key="test-key",
135
+ retry=RetryPolicy(max_retries=0),
136
+ transport=sdk_transport(web),
137
+ ) as sdk:
138
+ assert (await sdk.models.list()).models
139
+ result = await sdk.system_one(
140
+ state="Customer needs a refund.",
141
+ questions={"eligible": Noul(instructions="Is this a refund request?")},
142
+ )
143
+ assert result.raw_http_response.headers["x-openjev-effort"] == "high"
144
+ assert result.raw_http_response.headers["x-openjev-executed-layers"] == "32"
145
+
146
+ asyncio.run(run())
147
+ assert calls[0][0] == "high"
148
+
149
+
150
+ def test_auth_and_invalid_sdk_extra_body(service):
151
+ web, calls = service
152
+ assert web.get("/v1/models").status_code == 401
153
+ body = {"state": "ticket", "questions": {"q": {"type": "noul"}}}
154
+ headers = {"Authorization": "Bearer test-key"}
155
+ for extra in (
156
+ {"effort": "medium"},
157
+ {"unexpected": True},
158
+ {"model": []},
159
+ {"effort": {}},
160
+ ):
161
+ assert web.post(
162
+ "/v1/systemone", json={**body, **extra}, headers=headers
163
+ ).status_code in (404, 422)
164
+ assert calls == []
165
+
166
+
167
+ def test_exact_prompt_plain_choice_and_canonical_noul():
168
+ criterion = {c["id"]: c["description"] for c in BINARY_CRITERIA}
169
+ q = prepare_request(
170
+ {
171
+ "state": "state",
172
+ "questions": {
173
+ "q": {"type": "choice", "instructions": "task", "criteria": criterion}
174
+ },
175
+ },
176
+ model_name="apus",
177
+ )[0]
178
+ assert q.record["criteria"] == BINARY_CRITERIA
179
+ nq = prepare_request(
180
+ {
181
+ "state": "state",
182
+ "questions": {
183
+ "q": {
184
+ "type": "noul",
185
+ "instructions": "task",
186
+ "criteria": {
187
+ "true": BINARY_CRITERIA[0]["description"],
188
+ "false": BINARY_CRITERIA[1]["description"],
189
+ },
190
+ }
191
+ },
192
+ },
193
+ model_name="apus",
194
+ )[0]
195
+ assert nq.record["instructions"] == "task"
196
+ assert nq.record["primitive"] == "noul"
197
+ with pytest.raises(Exception, match="budget"):
198
+ compile_question(nq, Tokenizer(), max_tokens=5)
199
+
200
+
201
+ def test_no_silent_low_same_engine():
202
+ with pytest.raises(ValueError, match="distinct engine"):
203
+ Settings("http://same:80", "http://same:80/", "high", "low", "key")
204
+ with pytest.raises(ValueError, match="aliases"):
205
+ Settings("http://a", "http://b", "same", "same", "key")
206
+
207
+
208
+ def test_missing_backend_candidates_fails():
209
+ settings = Settings("http://a", "http://b", "high", "low", "key")
210
+ transport = httpx.MockTransport(
211
+ lambda req: httpx.Response(
212
+ 200, json={"choices": [{"logprobs": {"top_logprobs": [{}]}}]}
213
+ )
214
+ )
215
+ with TestClient(
216
+ create_app(settings, Tokenizer(), transport=transport, check_backends=False)
217
+ ) as web:
218
+ r = web.post(
219
+ "/v1/systemone",
220
+ headers={"Authorization": "Bearer key"},
221
+ json={"state": "x", "questions": {"q": {"type": "noul"}}},
222
+ )
223
+ assert r.status_code == 502
224
+ assert r.json()["error"]["code"] == "invalid_backend_scores"
225
+
226
+
227
+ def test_duplicate_json_and_oversized_fail(service):
228
+ web, _ = service
229
+ headers = {"Authorization": "Bearer test-key"}
230
+ assert (
231
+ web.post(
232
+ "/v1/systemone", headers=headers, content='{"state":"a","state":"b"}'
233
+ ).status_code
234
+ == 422
235
+ )
236
+ assert (
237
+ web.post("/v1/systemone", headers=headers, content="x" * 1_048_577).status_code
238
+ == 413
239
+ )
240
+
241
+
242
+ def test_deterministic_singleton_and_unknown_model(service):
243
+ web, calls = service
244
+ headers = {"Authorization": "Bearer test-key"}
245
+ base = {"state": "x", "questions": {"q": {"type": "score", "criteria": ["only"]}}}
246
+ response = web.post("/v1/systemone", headers=headers, json=base)
247
+ assert response.status_code == 200
248
+ assert response.json()["answers"]["q"]["score"] == 0
249
+ assert response.headers["x-openjev-executed-layers"] == "0"
250
+ assert calls == []
251
+ base.update(model="official-other")
252
+ assert web.post("/v1/systemone", headers=headers, json=base).status_code == 404
253
+
254
+
255
+ def artifact_settings(tmp_path):
256
+ from gateway import file_sha
257
+
258
+ high, low = tmp_path / "high", tmp_path / "low"
259
+ for root, depth in ((high, 32), (low, 16)):
260
+ root.mkdir()
261
+ (root / "config.json").write_text(
262
+ json.dumps(
263
+ {
264
+ "model_type": "qwen3_5",
265
+ "text_config": {
266
+ "num_hidden_layers": depth,
267
+ "hidden_size": 4096,
268
+ "layer_types": ["full_attention"] * depth,
269
+ },
270
+ }
271
+ )
272
+ )
273
+ (root / "model.safetensors.index.json").write_text("{}")
274
+ (high / "release-manifest.json").write_text("{}")
275
+ proof = {
276
+ "status": "verified",
277
+ "actual_depth": 16,
278
+ "all_retained_tensors_bit_identical": True,
279
+ }
280
+ for key, path in {
281
+ "export_config_sha256": low / "config.json",
282
+ "export_index_sha256": low / "model.safetensors.index.json",
283
+ "source_config_sha256": high / "config.json",
284
+ "source_index_sha256": high / "model.safetensors.index.json",
285
+ "source_release_manifest_sha256": high / "release-manifest.json",
286
+ }.items():
287
+ proof[key] = file_sha(path)
288
+ (low / "export-low-provenance.json").write_text(json.dumps(proof))
289
+ return Settings(
290
+ "http://high",
291
+ "http://low",
292
+ "high32",
293
+ "low16",
294
+ "key",
295
+ high_model_path=str(high),
296
+ low_model_path=str(low),
297
+ )
298
+
299
+
300
+ def test_artifact_depth_and_provenance_fail_closed(tmp_path):
301
+ from gateway import verify_artifacts
302
+
303
+ settings = artifact_settings(tmp_path)
304
+ assert verify_artifacts(settings)["low"] == tmp_path / "low"
305
+ cfg = tmp_path / "low" / "config.json"
306
+ cfg.write_text((tmp_path / "high" / "config.json").read_text())
307
+ with pytest.raises(ValueError, match="depth"):
308
+ verify_artifacts(settings)
309
+
310
+
311
+ def test_startup_binds_engine_root_to_verified_depth(tmp_path):
312
+ settings = artifact_settings(tmp_path)
313
+
314
+ def upstream(req):
315
+ # Wrong low root advertises high artifacts despite a distinct low alias.
316
+ alias = "low16" if req.url.host == "low" else "high32"
317
+ return httpx.Response(
318
+ 200, json={"data": [{"id": alias, "root": settings.high_model_path}]}
319
+ )
320
+
321
+ with pytest.raises(RuntimeError, match="alias/root"):
322
+ with TestClient(
323
+ create_app(settings, Tokenizer(), transport=httpx.MockTransport(upstream))
324
+ ):
325
+ pass
326
+
327
+
328
+ def test_sdk_rejects_unknown_extension_via_http(service):
329
+ from typesafe_sdk import TypeSafeUnprocessableEntityError
330
+
331
+ web, _ = service
332
+ with client(web) as sdk, pytest.raises(TypeSafeUnprocessableEntityError):
333
+ sdk.system_one(
334
+ state="Customer ticket", questions={"q": Noul()}, extra_body={"unknown": 1}
335
+ )
336
+
337
+
338
+ def test_live_smoke_script_with_real_sdk(service, tmp_path, monkeypatch):
339
+ import smoke_sdk
340
+
341
+ web, _ = service
342
+ monkeypatch.setenv("JEV_API_KEY", "test-key")
343
+ monkeypatch.setattr(
344
+ smoke_sdk,
345
+ "TypeSafeClient",
346
+ lambda **kw: TypeSafeClient(transport=sdk_transport(web), **kw),
347
+ )
348
+ monkeypatch.setattr(
349
+ smoke_sdk,
350
+ "AsyncTypeSafeClient",
351
+ lambda **kw: AsyncTypeSafeClient(transport=sdk_transport(web), **kw),
352
+ )
353
+ output = tmp_path / "smoke"
354
+ monkeypatch.setattr(sys, "argv", ["smoke_sdk.py", "--output", str(output)])
355
+ smoke_sdk.main()
356
+ report = json.loads((output / "summary.json").read_text())
357
+ assert report["status"] == "passed" and report["cases"] == 5
358
+ rows = [
359
+ json.loads(line)
360
+ for line in (output / "requests.jsonl").read_text().splitlines()
361
+ ]
362
+ assert rows[0]["response_headers"]["x-typesafe-request-id"]
363
+ assert rows[1]["request"]["questions"]["queue"]["type"] == "choice"
364
+ assert "test-key" not in (output / "requests.jsonl").read_text()
deployment/tests/test_vllm_depth_export.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """CPU proof: exported half-depth uses original layers, norm, and readout."""
2
+
3
+ import importlib.util
4
+ import json
5
+ from pathlib import Path
6
+
7
+ import pytest
8
+ import torch
9
+ from safetensors import safe_open
10
+ from safetensors.torch import save_file
11
+ from transformers import (
12
+ Qwen3_5Config,
13
+ Qwen3_5ForConditionalGeneration,
14
+ Qwen3_5TextConfig,
15
+ )
16
+
17
+
18
+ spec = importlib.util.spec_from_file_location(
19
+ "export_low", Path(__file__).parents[1] / "export_low.py"
20
+ )
21
+ module = importlib.util.module_from_spec(spec)
22
+ spec.loader.exec_module(module)
23
+
24
+
25
+ def test_actual_32_to_16_prefix_logits_and_saved_tensors(tmp_path):
26
+ torch.set_num_threads(1)
27
+ text = Qwen3_5TextConfig(
28
+ vocab_size=64,
29
+ hidden_size=32,
30
+ intermediate_size=48,
31
+ num_hidden_layers=32,
32
+ num_attention_heads=2,
33
+ num_key_value_heads=1,
34
+ head_dim=16,
35
+ linear_key_head_dim=8,
36
+ linear_value_head_dim=8,
37
+ linear_num_key_heads=2,
38
+ linear_num_value_heads=2,
39
+ layer_types=(["linear_attention"] * 3 + ["full_attention"]) * 8,
40
+ rope_parameters={
41
+ "rope_type": "default",
42
+ "rope_theta": 10000.0,
43
+ "partial_rotary_factor": 1.0,
44
+ "mrope_section": [2, 3, 3],
45
+ },
46
+ )
47
+ config = Qwen3_5Config(
48
+ text_config=text,
49
+ vision_config={
50
+ "depth": 1,
51
+ "hidden_size": 32,
52
+ "intermediate_size": 48,
53
+ "num_heads": 2,
54
+ "out_hidden_size": 32,
55
+ "num_position_embeddings": 16,
56
+ },
57
+ )
58
+ config._attn_implementation = "eager"
59
+ config.text_config._attn_implementation = "eager"
60
+ config.text_config.num_hidden_layers = 32
61
+ config.text_config.layer_types = (["linear_attention"] * 3 + ["full_attention"]) * 8
62
+ config.tie_word_embeddings = False
63
+ config.text_config.tie_word_embeddings = False
64
+ torch.manual_seed(47)
65
+ full = Qwen3_5ForConditionalGeneration(config).eval()
66
+ state = full.state_dict()
67
+ source, output = tmp_path / "source", tmp_path / "low"
68
+ source.mkdir()
69
+ output.mkdir()
70
+ save_file(state, source / "model.safetensors", metadata={"format": "pt"})
71
+ weight_map = {k: "model.safetensors" for k in state}
72
+ kept, removed = module.select_keys(weight_map, 16, 32)
73
+ assert len(removed) > 0
74
+ assert module.READOUT <= set(kept)
75
+ assert all(
76
+ not module.LAYER.match(k) or int(module.LAYER.match(k).group(1)) < 16
77
+ for k in kept
78
+ )
79
+ files, tensors = module.copy_weights(source, output, weight_map, kept)
80
+ short_config = module.prefix_config(config.to_dict())
81
+ assert short_config["vision_config"] == config.to_dict()["vision_config"]
82
+ assert short_config["text_config"]["num_hidden_layers"] == 16
83
+ assert len(short_config["text_config"]["layer_types"]) == 16
84
+ loaded = {}
85
+ for f in files:
86
+ with safe_open(output / f["file"], framework="pt") as handle:
87
+ loaded.update({key: handle.get_tensor(key) for key in handle.keys()})
88
+ low_config = Qwen3_5Config.from_dict(short_config)
89
+ low_config._attn_implementation = "eager"
90
+ low_config.text_config._attn_implementation = "eager"
91
+ low = Qwen3_5ForConditionalGeneration(low_config).eval()
92
+ low.load_state_dict(loaded, strict=True)
93
+ assert len(low.model.language_model.layers) == 16
94
+ assert len(tensors) == len(loaded)
95
+ for key in module.READOUT:
96
+ torch.testing.assert_close(loaded[key], state[key], rtol=0, atol=0)
97
+ ids, candidates = torch.tensor([[4, 5, 6, 7]]), torch.tensor([10, 11, 12])
98
+ with torch.no_grad():
99
+ captured = []
100
+ hook = full.model.language_model.layers[15].register_forward_hook(
101
+ lambda _module, _inputs, output: captured.append(output.detach().clone())
102
+ )
103
+ try:
104
+ full(input_ids=ids, use_cache=False)
105
+ finally:
106
+ hook.remove()
107
+ # Independently capture the full model's layer-16 residual, then apply
108
+ # its original final norm and head. No monorepo runtime dependency.
109
+ native = full.lm_head(
110
+ full.model.language_model.norm(captured[0][:, -1])
111
+ ).index_select(-1, candidates)
112
+ exported = (
113
+ low(input_ids=ids, use_cache=False)
114
+ .logits[:, -1]
115
+ .index_select(-1, candidates)
116
+ )
117
+ torch.testing.assert_close(native, exported, atol=1e-6, rtol=1e-5)
118
+ assert (
119
+ json.loads((output / "model.safetensors.index.json").read_text())["metadata"][
120
+ "total_size"
121
+ ]
122
+ > 0
123
+ )
124
+
125
+
126
+ @pytest.mark.parametrize("missing", sorted(module.READOUT))
127
+ def test_readout_cannot_be_dropped(missing):
128
+ keys = (module.READOUT - {missing}) | {
129
+ f"model.language_model.layers.{i}.weight" for i in range(32)
130
+ }
131
+ with pytest.raises(ValueError, match="missing"):
132
+ module.select_keys(keys, 16, 32)
133
+
134
+
135
+ def test_unknown_adapter_namespace_rejected():
136
+ keys = (
137
+ module.READOUT
138
+ | {f"model.language_model.layers.{i}.weight" for i in range(32)}
139
+ | {"readout.adapter.weight"}
140
+ )
141
+ with pytest.raises(ValueError, match="unexpected"):
142
+ module.select_keys(keys, 16, 32)
143
+
144
+
145
+ def test_existing_output_never_overwritten(tmp_path):
146
+ with pytest.raises(ValueError, match="new directory"):
147
+ module.export(tmp_path / "unused", tmp_path)
148
+
149
+
150
+ def test_source_stat_change_rejects_verified_publication(tmp_path):
151
+ path = tmp_path / "weights"
152
+ path.write_bytes(b"original")
153
+ snapshot = module.source_snapshot(tmp_path)
154
+ module.assert_source_unchanged(tmp_path, snapshot)
155
+ path.write_bytes(b"changed-longer")
156
+ with pytest.raises(ValueError, match="changed during export"):
157
+ module.assert_source_unchanged(tmp_path, snapshot)
158
+
159
+
160
+ def test_launcher_full_low_weight_hash_detects_tampering(tmp_path):
161
+ config = {
162
+ "model_type": "qwen3_5",
163
+ "text_config": {
164
+ "hidden_size": 4096,
165
+ "vocab_size": 248320,
166
+ "num_hidden_layers": 16,
167
+ "layer_types": ["linear_attention"] * 16,
168
+ },
169
+ }
170
+ (tmp_path / "config.json").write_text(json.dumps(config))
171
+ keys = module.READOUT | {
172
+ f"model.language_model.layers.{i}.weight" for i in range(16)
173
+ }
174
+ (tmp_path / "model.safetensors.index.json").write_text(
175
+ json.dumps({"weight_map": {key: "model.safetensors" for key in keys}})
176
+ )
177
+ shard = tmp_path / "model.safetensors"
178
+ shard.write_bytes(b"original-weights")
179
+ manifest = {
180
+ "status": "verified",
181
+ "actual_depth": 16,
182
+ "all_retained_tensors_bit_identical": True,
183
+ "export_config_sha256": module.file_sha(tmp_path / "config.json"),
184
+ "export_index_sha256": module.file_sha(
185
+ tmp_path / "model.safetensors.index.json"
186
+ ),
187
+ "export_weight_files": [
188
+ {
189
+ "file": shard.name,
190
+ "bytes": shard.stat().st_size,
191
+ "sha256": module.file_sha(shard),
192
+ }
193
+ ],
194
+ }
195
+ (tmp_path / "export-low-provenance.json").write_text(json.dumps(manifest))
196
+ module.verify_artifact(tmp_path, 16)
197
+ shard.write_bytes(b"tampered-weights")
198
+ with pytest.raises(ValueError, match="weight SHA mismatch"):
199
+ module.verify_artifact(tmp_path, 16)
200
+
201
+
202
+ def test_launcher_high_release_weight_hash_detects_tampering(tmp_path):
203
+ config = {
204
+ "model_type": "qwen3_5",
205
+ "architectures": ["Qwen3_5ForConditionalGeneration"],
206
+ "tie_word_embeddings": False,
207
+ "text_config": {
208
+ "hidden_size": 4096,
209
+ "vocab_size": 248320,
210
+ "intermediate_size": 12288,
211
+ "num_hidden_layers": 32,
212
+ "tie_word_embeddings": False,
213
+ "layer_types": (["linear_attention"] * 3 + ["full_attention"]) * 8,
214
+ },
215
+ }
216
+ (tmp_path / "config.json").write_text(json.dumps(config))
217
+ (tmp_path / "depth_config.json").write_text(
218
+ json.dumps({"exit_depth": 16, "full_depth": 32})
219
+ )
220
+ for name in ("tokenizer.json", "tokenizer_config.json", "chat_template.jinja"):
221
+ (tmp_path / name).write_text("mock tokenization asset")
222
+ keys = module.READOUT | {
223
+ f"model.language_model.layers.{i}.weight" for i in range(32)
224
+ }
225
+ (tmp_path / "model.safetensors.index.json").write_text(
226
+ json.dumps({"weight_map": {key: "model.safetensors" for key in keys}})
227
+ )
228
+ shard = tmp_path / "model.safetensors"
229
+ shard.write_bytes(b"original-weights")
230
+ entries = [
231
+ {"path": p.name, "sha256": module.file_sha(p)} for p in tmp_path.iterdir()
232
+ ]
233
+ (tmp_path / "release-manifest.json").write_text(json.dumps({"files": entries}))
234
+ module.verify_artifact(tmp_path, 32)
235
+ shard.write_bytes(b"tampered-weights")
236
+ with pytest.raises(ValueError, match="weight SHA mismatch"):
237
+ module.verify_artifact(tmp_path, 32)