veritiana-ai commited on
Commit
811d51e
·
verified ·
1 Parent(s): 50540ff

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -1,35 +1,2 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
  *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  *.onnx filter=lfs diff=lfs merge=lfs -text
2
+ *.png filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ .venv/
2
+ __pycache__/
3
+ *.pyc
4
+ node_modules/
5
+ dist/
CITATION.cff ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ cff-version: 1.2.0
2
+ title: Veritiana Prompt Task & Complexity Classifier
3
+ message: If you use this model, please cite this software release.
4
+ type: software
5
+ authors:
6
+ - name: Veritiana AI
7
+ version: 3.1.0
8
+ date-released: 2026-08-05
9
+ license: Apache-2.0
10
+ url: https://www.veritiana.com/prompt_classifier.html
LICENSE ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
203
+
204
+ Copyright 2026 Veritiana
NOTICE ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Veritiana Prompt Task & Complexity Classifier v3.1.0
2
+ Copyright 2026 Veritiana
3
+
4
+ Model and software release license: Apache License 2.0.
5
+
6
+ Training sources used by the recorded preparation run:
7
+ - OpenAssistant/oasst1 — Apache License 2.0
8
+ - grammarly/coedit — Apache License 2.0
9
+ - evalplus/mbppplus — Apache License 2.0
10
+ - google/IFEval — Apache License 2.0
11
+
12
+ The source datasets are not redistributed in this repository. Their original authors retain attribution and rights in their respective works. Training used prompt/instruction fields only as described in dataset-manifest.json.
13
+
14
+ Provenance limitation: the exact Hugging Face dataset revisions were not captured by the original download run. Exact reproduction of the released ONNX SHA-256 therefore requires the archived dataset.jsonl whose SHA-256 is recorded in dataset-manifest.json.
README.md CHANGED
@@ -1,3 +1,297 @@
1
  ---
2
  license: apache-2.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
  license: apache-2.0
3
+ language:
4
+ - en
5
+ pipeline_tag: text-classification
6
+ inference: false
7
+ tags:
8
+ - onnx
9
+ - onnxruntime
10
+ - browser
11
+ - local-inference
12
+ - prompt-classification
13
+ - task-classification
14
+ - complexity-estimation
15
+ - ai-routing
16
+ - green-ai
17
+ - veritiana
18
+ datasets:
19
+ - OpenAssistant/oasst1
20
+ - grammarly/coedit
21
+ - evalplus/mbppplus
22
+ - google/IFEval
23
+ metrics:
24
+ - accuracy
25
+ - f1
26
  ---
27
+
28
+ # Veritiana Prompt Task & Complexity Classifier
29
+
30
+ ![Veritiana Prompt Classifier architecture](assets/veritiana-prompt-classifier-architecture.png)
31
+
32
+ A compact ONNX classifier from **Veritiana AI** that identifies the type of work requested by an AI prompt and estimates its complexity **before generative execution begins**.
33
+
34
+ The model runs locally, including directly in a browser through ONNX Runtime Web. It does not generate text and does not require prompt content to be sent to a remote classification service.
35
+
36
+ **Live browser test:** https://www.veritiana.com/prompt_classifier.html
37
+
38
+ ## What it returns
39
+
40
+ The model produces two independent probability distributions.
41
+
42
+ ### Task — 9 classes
43
+
44
+ `general_chat`, `writing`, `translation`, `summarization`, `research`, `coding`, `mathematics`, `document_analysis`, `high_stakes`
45
+
46
+ ### Complexity — 3 classes
47
+
48
+ `low`, `medium`, `high`
49
+
50
+ Example result:
51
+
52
+ ```text
53
+ task: coding
54
+ complexity: high
55
+ ```
56
+
57
+ ## Intended role
58
+
59
+ This release is a public recognition layer, not a complete AI router.
60
+
61
+ ```text
62
+ Prompt
63
+ → local feature extraction
64
+ → ONNX task and complexity classifier
65
+ → probabilities and confidence
66
+ → future routing policy
67
+ → model, tools, context, safeguards and budget
68
+ ```
69
+
70
+ The open model answers **what kind of request this is** and **how demanding it appears**. Commercial routing value begins when those signals are combined with provider capabilities, prices, latency, organizational policies, tool access, execution quality and feedback history to decide how the request should be executed.
71
+
72
+ The classifier is also part of the product direction behind **Veritiana AI Meter**, where local recognition helps distinguish the type and estimated complexity of visible AI work.
73
+
74
+ ## Model architecture
75
+
76
+ The input is a fixed vector of 1,544 float32 features:
77
+
78
+ - 1,024 hashed word and word-bigram bins
79
+ - 512 hashed character-trigram bins
80
+ - 8 numerical and structural features
81
+
82
+ Two independent multinomial logistic-regression heads produce task and complexity probabilities.
83
+
84
+ ```text
85
+ input
86
+ features float32 [batch, 1544]
87
+
88
+ outputs
89
+ task_probabilities float32 [batch, 9]
90
+ complexity_probabilities float32 [batch, 3]
91
+ ```
92
+
93
+ The released ONNX model is approximately 75 KB and uses ONNX opset 13.
94
+
95
+ ## Feature contract
96
+
97
+ The feature extractor is part of the model contract. Raw text cannot be passed directly to `model.onnx`.
98
+
99
+ The repository includes the exact implementation in `features.py`, with matching browser logic in `examples/browser/src/main.js`.
100
+
101
+ The eight numerical features encode:
102
+
103
+ 1. normalized character length
104
+ 2. normalized word count
105
+ 3. question-mark count
106
+ 4. newline count
107
+ 5. technical-symbol count
108
+ 6. URL presence
109
+ 7. document/file-term presence
110
+ 8. constant bias value
111
+
112
+ Hashing uses FNV-1a 32-bit over JavaScript UTF-16 code units so Python and browser implementations remain compatible.
113
+
114
+ ## Internal evaluation
115
+
116
+ The recorded internal split contains 1,287 examples.
117
+
118
+ | Output | Accuracy | Macro-F1 |
119
+ |---|---:|---:|
120
+ | Task | 91.22% | 91.04% |
121
+ | Complexity | 86.79% | 87.24% |
122
+
123
+ These are **internal weak-label evaluation results**, not independently established real-world accuracy.
124
+
125
+ Important limitations:
126
+
127
+ - labels include source-fixed labels, deterministic weak rules and deterministic lexical augmentation
128
+ - augmented rows are variants, not independent human examples
129
+ - minority-class support is uneven
130
+ - the translation test support is only 4 examples and its reported score is not meaningful as a production claim
131
+ - multilingual behavior has not been independently benchmarked
132
+ - the classifier does not semantically reason like a large language model
133
+
134
+ Full per-class reports and confusion matrices are in `evaluation.json`.
135
+
136
+ ## Training data
137
+
138
+ The original normalized import contained 19,087 prompts:
139
+
140
+ | Source | Imported rows | Recorded use |
141
+ |---|---:|---|
142
+ | OpenAssistant/oasst1 | 10,170 | filtered root prompter messages |
143
+ | grammarly/coedit | 8,000 | source text, task fixed to writing |
144
+ | evalplus/mbppplus | 376 | prompt field, task fixed to coding |
145
+ | google/IFEval | 541 | prompt field, weak task labels and instruction-count complexity signal |
146
+
147
+ After filtering, balancing and deterministic augmentation, the prepared set contained 6,349 rows.
148
+
149
+ The datasets themselves are not redistributed. Data provenance, row counts, SHA-256 values and the known revision limitation are documented in `dataset-manifest.json`.
150
+
151
+ ## Browser use
152
+
153
+ A complete minimal Vite example is included in `examples/browser`.
154
+
155
+ ```bash
156
+ cd examples/browser
157
+ npm install
158
+ npm run dev
159
+ ```
160
+
161
+ The example loads `model.onnx`, reproduces the 1,544-feature contract and prints both probability distributions.
162
+
163
+ For the complete production interface, use the live Veritiana test:
164
+
165
+ https://www.veritiana.com/prompt_classifier.html
166
+
167
+ ## Python use
168
+
169
+ ```bash
170
+ python -m venv .venv
171
+ source .venv/bin/activate
172
+ pip install -r requirements-inference.txt
173
+ python examples/python/classify.py "Refactor this API and add rollback tests."
174
+ ```
175
+
176
+ Expected output structure:
177
+
178
+ ```json
179
+ {
180
+ "task": {
181
+ "label": "coding",
182
+ "confidence": 0.0,
183
+ "probabilities": {}
184
+ },
185
+ "complexity": {
186
+ "label": "high",
187
+ "confidence": 0.0,
188
+ "probabilities": {}
189
+ }
190
+ }
191
+ ```
192
+
193
+ The numerical values depend on the supplied prompt. The example does not send data to an external service.
194
+
195
+ ## Reproducing training
196
+
197
+ Exact reproduction requires either:
198
+
199
+ - `dataset.jsonl` with SHA-256 `1d56520b25809488fa8be91bcd14219accdb31146e1ca56686ff805d34c21649`, or
200
+ - prepared `training-balanced.jsonl` with SHA-256 `ab2665fcd75800b2350593b7af4222e81d8cc93a34f2bf67320514a92526f44c`.
201
+
202
+ The datasets are not included in this repository.
203
+
204
+ ```bash
205
+ python -m venv .venv
206
+ source .venv/bin/activate
207
+ pip install -r requirements-training.txt
208
+
209
+ python prepare_training_set.py dataset.jsonl \
210
+ --output training-balanced.jsonl \
211
+ --report training-balanced-report.json \
212
+ --seed 42 \
213
+ --confidence 0.65 \
214
+ --target-per-task 800 \
215
+ --max-per-task 2000
216
+
217
+ python train.py training-balanced.jsonl \
218
+ --output-dir reproduced-output \
219
+ --version 3.1.0-multisource-balanced \
220
+ --test-size 0.20 \
221
+ --seed 42 \
222
+ --c 4.0
223
+ ```
224
+
225
+ Reference model SHA-256:
226
+
227
+ ```text
228
+ dca7560742f19207d089d469c2907e7a1b7c06e2bbf14d2404a5d9359043f572
229
+ ```
230
+
231
+ Exact byte reproduction can also depend on the numerical environment and BLAS implementation. The feature contract and output probabilities are the primary compatibility target.
232
+
233
+ ## Repository contents
234
+
235
+ ```text
236
+ README.md
237
+ model.onnx
238
+ config.json
239
+ classifier-meta.json
240
+ evaluation.json
241
+ dataset-manifest.json
242
+ training-config.json
243
+ features.py
244
+ prepare_training_set.py
245
+ train.py
246
+ requirements-inference.txt
247
+ requirements-training.txt
248
+ LICENSE
249
+ NOTICE
250
+ assets/
251
+ veritiana-prompt-classifier-architecture.png
252
+ examples/
253
+ python/
254
+ browser/
255
+ upload_to_hub.py
256
+ ```
257
+
258
+ ## Appropriate uses
259
+
260
+ - local prompt analytics
261
+ - task-distribution measurement
262
+ - complexity estimation
263
+ - pre-routing signals
264
+ - browser-native AI usage instrumentation
265
+ - research and prototyping around deterministic pre-LLM decision layers
266
+
267
+ ## Out-of-scope uses
268
+
269
+ Do not use the classifier as:
270
+
271
+ - a medical, legal, financial or safety decision maker
272
+ - proof that a prompt is safe
273
+ - a factual-answer evaluator
274
+ - an authorization layer
275
+ - a universal language detector
276
+ - a guaranteed model-selection engine
277
+ - a replacement for human review in high-stakes workflows
278
+
279
+ ## Privacy
280
+
281
+ Inference can run entirely on the user device. The model itself does not upload, store or transmit prompt content. Integrators remain responsible for the behavior of the surrounding application.
282
+
283
+ ## License
284
+
285
+ The model and repository code are released under Apache License 2.0. See `LICENSE` and `NOTICE`.
286
+
287
+ ## Citation
288
+
289
+ ```bibtex
290
+ @software{veritiana_prompt_classifier_2026,
291
+ title = {Veritiana Prompt Task and Complexity Classifier},
292
+ author = {Veritiana AI},
293
+ year = {2026},
294
+ version = {3.1.0},
295
+ url = {https://www.veritiana.com/prompt_classifier.html}
296
+ }
297
+ ```
SHA256SUMS ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ffda9ef77ab85b9d12041edee241d106a4761b93a25ab5c312eb16450c844080 .gitattributes
2
+ 123fc16609d7b9cc84dabfd9ddd304b9d5b729a52efc6bcf4cec58fdfc2a7624 .gitignore
3
+ 7b959ae91f75e218db985d565a35abed62dbf4fa1a1573be3d7484e0f01190a1 CITATION.cff
4
+ 601ee540b197abb7f596f6138cc2845158b48bbad1afd364e0aef3a745fe8153 LICENSE
5
+ 0e2951233817f9692d784c33f5206f3120e8dbf5fdddf187d32f32a2f1bebf39 NOTICE
6
+ 55e89f0b8919d40e57bf52ae8785cce815c74d26df858f8aea88786fe231f536 README.md
7
+ 561550c1010619147e6190604adb0f239068eae27439e578c04102b0b2d410b6 UPLOAD.md
8
+ 5b533b27eea3e364070f5fb31c1bcab73efcbe02c5a31244c187954fd6845a9c assets/veritiana-prompt-classifier-architecture.png
9
+ 794da8ad12b6382117724f9ef84ee7a4869090d604b7ca4b98d32dfc2a781a05 classifier-meta.json
10
+ 05c2cc137b882f2fcd787a37a5044da7e162b4bcfc987b5060a89aebde137fac config.json
11
+ cb9e983f679a87479bf4f5cbe47ebcc816a1cb8f31ba8ccfbd0b7f3c856317c1 dataset-manifest.json
12
+ 163c7c98362e88a7a6cce2f6523d6f64f58a1f19afe2b0e09d69e319244c4986 evaluation.json
13
+ 4aeeafe4041d6489ae6c7caa81802ad48cd0734992f47f5e1d0d669e012489e4 examples/browser/README.md
14
+ 5b3ebb10fd2817dca98f6d8a8278062c3d6574ea3f97fc57a1d8a502f4315c08 examples/browser/index.html
15
+ 6df3e570901c32423204c6eb672387677840c8226712e221ab259c220fa48f42 examples/browser/package.json
16
+ aa0fefc04cfe62222760b44cadfbf63a519f58ed4de799ac23ca0efb045a9fb2 examples/browser/src/main.js
17
+ e5b1fea5c12849246fb69ce467d0dc38f892bcb7f65ddf1c3af7a1a001a61489 examples/browser/src/style.css
18
+ 2e8b8142dc7f29326fa98ca3b2ad43a7e6e8aa07ea7c6bf00bb4232508a8a7f7 examples/python/classify.py
19
+ f232bf95cdb53088b1dc15df7b6a0ca174ba04b930b0f0baaa1d1cbb29bdfa15 features.py
20
+ dca7560742f19207d089d469c2907e7a1b7c06e2bbf14d2404a5d9359043f572 model.onnx
21
+ 3330c8077f015bb153b3865a5994cdc0151f61975a1a46b9c93c948926be9a1c prepare_training_set.py
22
+ ed89e010a3e30b99e4ffb83898328d98fc471573922d4fc734267ac53a8bada5 requirements-inference.txt
23
+ b893fa86c1d13abad802057d4c0702900f321cd0bec41700b756c3bc32e5ac51 requirements-training.txt
24
+ e661b27c32be22e4b3064eb4b46c4e5453c31bc5f0d1983f223cfa48770446ce requirements-upload.txt
25
+ 5df357c377930a256c56d2a3acc2b083feae17a6cd82d375d2e9c86f123903b8 train.py
26
+ a3768583ffae24d9b875fe577eb8a44e09614088460a2a5013a8c9427fe2c7a0 training-config.json
27
+ 2bcf8d1d7fafb54f440d3a218760fb0847e39e4c639d1a66a404f0e0937e2471 upload_to_hub.py
UPLOAD.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Upload instructions
2
+
3
+ Recommended repository ID:
4
+
5
+ ```text
6
+ Veritiana-AI/prompt-task-complexity-classifier
7
+ ```
8
+
9
+ ## Web upload
10
+
11
+ Create a new **Model** repository on Hugging Face and upload the contents of this directory. Upload the files themselves, not the enclosing ZIP directory.
12
+
13
+ ## CLI upload
14
+
15
+ ```bash
16
+ python -m venv .venv
17
+ source .venv/bin/activate
18
+ pip install -r requirements-upload.txt
19
+ huggingface-cli login
20
+ python upload_to_hub.py Veritiana-AI/prompt-task-complexity-classifier
21
+ ```
22
+
23
+ After upload, confirm:
24
+
25
+ - `README.md` renders the architecture image
26
+ - `model.onnx` SHA-256 is `dca7560742f19207d089d469c2907e7a1b7c06e2bbf14d2404a5d9359043f572`
27
+ - the live demo link opens `https://www.veritiana.com/prompt_classifier.html`
28
+ - the license displays as Apache-2.0
29
+ - the repository does not show an automatic inference widget, because raw text requires the custom feature extractor
assets/veritiana-prompt-classifier-architecture.png ADDED

Git LFS Details

  • SHA256: 5b533b27eea3e364070f5fb31c1bcab73efcbe02c5a31244c187954fd6845a9c
  • Pointer size: 132 Bytes
  • Size of remote file: 1.41 MB
classifier-meta.json ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model": "Veritiana Prompt Task & Complexity Classifier",
3
+ "version": "3.1.0-multisource-balanced",
4
+ "engine": "ONNX Runtime Web",
5
+ "architecture": "1544 hashed lexical/character/numeric features; dual multinomial logistic heads",
6
+ "input": {
7
+ "name": "features",
8
+ "dtype": "float32",
9
+ "shape": [
10
+ "batch",
11
+ 1544
12
+ ]
13
+ },
14
+ "outputs": [
15
+ {
16
+ "name": "task_probabilities",
17
+ "dtype": "float32",
18
+ "shape": [
19
+ "batch",
20
+ 9
21
+ ]
22
+ },
23
+ {
24
+ "name": "complexity_probabilities",
25
+ "dtype": "float32",
26
+ "shape": [
27
+ "batch",
28
+ 3
29
+ ]
30
+ }
31
+ ],
32
+ "task_labels": [
33
+ "general_chat",
34
+ "writing",
35
+ "translation",
36
+ "summarization",
37
+ "research",
38
+ "coding",
39
+ "mathematics",
40
+ "document_analysis",
41
+ "high_stakes"
42
+ ],
43
+ "complexity_labels": [
44
+ "low",
45
+ "medium",
46
+ "high"
47
+ ],
48
+ "training_data": {
49
+ "sources": [
50
+ "OpenAssistant/oasst1",
51
+ "grammarly/coedit",
52
+ "evalplus/mbppplus",
53
+ "google/IFEval"
54
+ ],
55
+ "balanced_rows": 6349,
56
+ "train_rows": 5062,
57
+ "test_rows": 1287,
58
+ "notes": "Low-confidence weak labels excluded; dominant tasks capped; deterministic lexical augmentations grouped with their source during split."
59
+ },
60
+ "validation": {
61
+ "task_accuracy": 0.9121989121989122,
62
+ "task_macro_f1": 0.9103734961055048,
63
+ "complexity_accuracy": 0.8679098679098679,
64
+ "complexity_macro_f1": 0.8723688079771166,
65
+ "runtime": "AI Meter bundled ONNX Runtime Web 1.22.0",
66
+ "runtime_max_abs_diff": 1.1920928955078125e-07
67
+ },
68
+ "onnx_sha256": "dca7560742f19207d089d469c2907e7a1b7c06e2bbf14d2404a5d9359043f572",
69
+ "limitations": "Validation uses weak labels and deterministic augmentations. Do not present these metrics as independent human-ground-truth production accuracy.",
70
+ "model_file": "model.onnx",
71
+ "live_demo": "https://www.veritiana.com/prompt_classifier.html",
72
+ "intended_role": "Local task and complexity recognition before generative execution; input signal for future routing policies."
73
+ }
config.json ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "model_type": "veritiana_prompt_classifier",
3
+ "model_version": "3.1.0-multisource-balanced",
4
+ "architecture": "dual_multinomial_logistic_regression",
5
+ "model_file": "model.onnx",
6
+ "input": {
7
+ "name": "features",
8
+ "dtype": "float32",
9
+ "shape": [
10
+ "batch",
11
+ 1544
12
+ ]
13
+ },
14
+ "outputs": {
15
+ "task_probabilities": {
16
+ "dtype": "float32",
17
+ "shape": [
18
+ "batch",
19
+ 9
20
+ ]
21
+ },
22
+ "complexity_probabilities": {
23
+ "dtype": "float32",
24
+ "shape": [
25
+ "batch",
26
+ 3
27
+ ]
28
+ }
29
+ },
30
+ "features": {
31
+ "total": 1544,
32
+ "word_bigram_bins": 1024,
33
+ "character_trigram_bins": 512,
34
+ "numeric_features": 8,
35
+ "hash": "FNV-1a 32-bit over JavaScript UTF-16 code units"
36
+ },
37
+ "task_labels": [
38
+ "general_chat",
39
+ "writing",
40
+ "translation",
41
+ "summarization",
42
+ "research",
43
+ "coding",
44
+ "mathematics",
45
+ "document_analysis",
46
+ "high_stakes"
47
+ ],
48
+ "complexity_labels": [
49
+ "low",
50
+ "medium",
51
+ "high"
52
+ ],
53
+ "thresholds": {
54
+ "task_classified": 0.72,
55
+ "complexity_classified": 0.66,
56
+ "likely": 0.45
57
+ },
58
+ "runtime": {
59
+ "browser": "ONNX Runtime Web 1.22.0",
60
+ "onnx_opset": 13
61
+ },
62
+ "live_demo": "https://www.veritiana.com/prompt_classifier.html"
63
+ }
dataset-manifest.json ADDED
@@ -0,0 +1,88 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "manifest_version": "1.0",
3
+ "model_version": "3.1.0-multisource-balanced",
4
+ "source_dataset": {
5
+ "file": "dataset.jsonl (not redistributed in this release)",
6
+ "sha256": "1d56520b25809488fa8be91bcd14219accdb31146e1ca56686ff805d34c21649",
7
+ "rows": 19087,
8
+ "source_revisions": "Exact Hugging Face revisions were not captured by the original download run."
9
+ },
10
+ "prepared_dataset": {
11
+ "file": "training-balanced.jsonl (not redistributed in this release)",
12
+ "sha256": "ab2665fcd75800b2350593b7af4222e81d8cc93a34f2bf67320514a92526f44c",
13
+ "rows": 6349
14
+ },
15
+ "sources": [
16
+ {
17
+ "name": "OpenAssistant/oasst1",
18
+ "url": "https://huggingface.co/datasets/OpenAssistant/oasst1",
19
+ "license": "Apache-2.0",
20
+ "rows_after_import": 10170,
21
+ "use": "root prompter messages after filters"
22
+ },
23
+ {
24
+ "name": "grammarly/coedit",
25
+ "url": "https://huggingface.co/datasets/grammarly/coedit",
26
+ "license": "Apache-2.0",
27
+ "rows_after_import": 8000,
28
+ "use": "src field only; task fixed to writing"
29
+ },
30
+ {
31
+ "name": "evalplus/mbppplus",
32
+ "url": "https://huggingface.co/datasets/evalplus/mbppplus",
33
+ "license": "Apache-2.0",
34
+ "rows_after_import": 376,
35
+ "use": "prompt field only; task fixed to coding"
36
+ },
37
+ {
38
+ "name": "google/IFEval",
39
+ "url": "https://huggingface.co/datasets/google/IFEval",
40
+ "license": "Apache-2.0",
41
+ "rows_after_import": 541,
42
+ "use": "prompt field; weak task labels; instruction count informs complexity"
43
+ }
44
+ ],
45
+ "preparation_report": {
46
+ "input_rows": 19087,
47
+ "accepted_rows": 10696,
48
+ "rejected_low_confidence": 8391,
49
+ "output_rows": 6349,
50
+ "confidence_threshold": 0.65,
51
+ "by_task": {
52
+ "writing": 2000,
53
+ "coding": 1228,
54
+ "general_chat": 800,
55
+ "mathematics": 410,
56
+ "summarization": 139,
57
+ "high_stakes": 425,
58
+ "translation": 92,
59
+ "document_analysis": 455,
60
+ "research": 800
61
+ },
62
+ "by_complexity": {
63
+ "low": 4307,
64
+ "medium": 1816,
65
+ "high": 226
66
+ },
67
+ "by_label_method": {
68
+ "source_fixed": 2216,
69
+ "weak_rule": 1810,
70
+ "deterministic_augmentation": 2323
71
+ },
72
+ "augmented_by_task": {
73
+ "general_chat": 575,
74
+ "translation": 74,
75
+ "summarization": 112,
76
+ "research": 526,
77
+ "mathematics": 328,
78
+ "document_analysis": 367,
79
+ "high_stakes": 341
80
+ },
81
+ "warning": "Augmented rows are lexical variants, not independent human examples."
82
+ },
83
+ "model": {
84
+ "file": "model.onnx",
85
+ "sha256": "dca7560742f19207d089d469c2907e7a1b7c06e2bbf14d2404a5d9359043f572",
86
+ "bytes": 75346
87
+ }
88
+ }
evaluation.json ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "summary": {
3
+ "task_accuracy": 0.9121989121989122,
4
+ "task_macro_f1": 0.9103734961055048,
5
+ "complexity_accuracy": 0.8679098679098679,
6
+ "complexity_macro_f1": 0.8723688079771166,
7
+ "training_seconds": 13.171398878097534
8
+ },
9
+ "task_report": {
10
+ "general_chat": {
11
+ "precision": 0.9310344827586207,
12
+ "recall": 0.9204545454545454,
13
+ "f1-score": 0.9257142857142857,
14
+ "support": 176.0
15
+ },
16
+ "writing": {
17
+ "precision": 0.974025974025974,
18
+ "recall": 0.9566326530612245,
19
+ "f1-score": 0.9652509652509652,
20
+ "support": 392.0
21
+ },
22
+ "translation": {
23
+ "precision": 1.0,
24
+ "recall": 1.0,
25
+ "f1-score": 1.0,
26
+ "support": 4.0
27
+ },
28
+ "summarization": {
29
+ "precision": 0.9655172413793104,
30
+ "recall": 0.9655172413793104,
31
+ "f1-score": 0.9655172413793104,
32
+ "support": 29.0
33
+ },
34
+ "research": {
35
+ "precision": 0.8372093023255814,
36
+ "recall": 0.8944099378881988,
37
+ "f1-score": 0.8648648648648649,
38
+ "support": 161.0
39
+ },
40
+ "coding": {
41
+ "precision": 0.9132231404958677,
42
+ "recall": 0.9057377049180327,
43
+ "f1-score": 0.9094650205761317,
44
+ "support": 244.0
45
+ },
46
+ "mathematics": {
47
+ "precision": 0.8641975308641975,
48
+ "recall": 0.8536585365853658,
49
+ "f1-score": 0.8588957055214724,
50
+ "support": 82.0
51
+ },
52
+ "document_analysis": {
53
+ "precision": 0.8165137614678899,
54
+ "recall": 0.9081632653061225,
55
+ "f1-score": 0.8599033816425121,
56
+ "support": 98.0
57
+ },
58
+ "high_stakes": {
59
+ "precision": 0.8901098901098901,
60
+ "recall": 0.801980198019802,
61
+ "f1-score": 0.84375,
62
+ "support": 101.0
63
+ },
64
+ "accuracy": 0.9121989121989122,
65
+ "macro avg": {
66
+ "precision": 0.9102034803808147,
67
+ "recall": 0.9118393425125113,
68
+ "f1-score": 0.9103734961055048,
69
+ "support": 1287.0
70
+ },
71
+ "weighted avg": {
72
+ "precision": 0.913815881736539,
73
+ "recall": 0.9121989121989122,
74
+ "f1-score": 0.912490699441833,
75
+ "support": 1287.0
76
+ }
77
+ },
78
+ "complexity_report": {
79
+ "low": {
80
+ "precision": 0.9166666666666666,
81
+ "recall": 0.8856476079346558,
82
+ "f1-score": 0.9008902077151335,
83
+ "support": 857.0
84
+ },
85
+ "medium": {
86
+ "precision": 0.7555555555555555,
87
+ "recall": 0.8225806451612904,
88
+ "f1-score": 0.7876447876447876,
89
+ "support": 372.0
90
+ },
91
+ "high": {
92
+ "precision": 0.9629629629629629,
93
+ "recall": 0.896551724137931,
94
+ "f1-score": 0.9285714285714286,
95
+ "support": 58.0
96
+ },
97
+ "accuracy": 0.8679098679098679,
98
+ "macro avg": {
99
+ "precision": 0.878395061728395,
100
+ "recall": 0.8682599924112924,
101
+ "f1-score": 0.8723688079771166,
102
+ "support": 1287.0
103
+ },
104
+ "weighted avg": {
105
+ "precision": 0.8721848110736999,
106
+ "recall": 0.8679098679098679,
107
+ "f1-score": 0.8694047489299714,
108
+ "support": 1287.0
109
+ }
110
+ },
111
+ "task_confusion_matrix": [
112
+ [
113
+ 162,
114
+ 2,
115
+ 0,
116
+ 0,
117
+ 1,
118
+ 3,
119
+ 4,
120
+ 0,
121
+ 4
122
+ ],
123
+ [
124
+ 1,
125
+ 375,
126
+ 0,
127
+ 0,
128
+ 5,
129
+ 10,
130
+ 1,
131
+ 0,
132
+ 0
133
+ ],
134
+ [
135
+ 0,
136
+ 0,
137
+ 4,
138
+ 0,
139
+ 0,
140
+ 0,
141
+ 0,
142
+ 0,
143
+ 0
144
+ ],
145
+ [
146
+ 0,
147
+ 0,
148
+ 0,
149
+ 28,
150
+ 0,
151
+ 0,
152
+ 1,
153
+ 0,
154
+ 0
155
+ ],
156
+ [
157
+ 4,
158
+ 1,
159
+ 0,
160
+ 1,
161
+ 144,
162
+ 3,
163
+ 0,
164
+ 5,
165
+ 3
166
+ ],
167
+ [
168
+ 3,
169
+ 5,
170
+ 0,
171
+ 0,
172
+ 6,
173
+ 221,
174
+ 0,
175
+ 6,
176
+ 3
177
+ ],
178
+ [
179
+ 0,
180
+ 0,
181
+ 0,
182
+ 0,
183
+ 4,
184
+ 2,
185
+ 70,
186
+ 6,
187
+ 0
188
+ ],
189
+ [
190
+ 4,
191
+ 0,
192
+ 0,
193
+ 0,
194
+ 5,
195
+ 0,
196
+ 0,
197
+ 89,
198
+ 0
199
+ ],
200
+ [
201
+ 0,
202
+ 2,
203
+ 0,
204
+ 0,
205
+ 7,
206
+ 3,
207
+ 5,
208
+ 3,
209
+ 81
210
+ ]
211
+ ],
212
+ "complexity_confusion_matrix": [
213
+ [
214
+ 759,
215
+ 98,
216
+ 0
217
+ ],
218
+ [
219
+ 64,
220
+ 306,
221
+ 2
222
+ ],
223
+ [
224
+ 5,
225
+ 1,
226
+ 52
227
+ ]
228
+ ]
229
+ }
examples/browser/README.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # Browser example
2
+
3
+ This Vite example performs local ONNX Runtime Web inference and implements the same 1,544-feature contract as the released model.
4
+
5
+ ```bash
6
+ npm install
7
+ npm run dev
8
+ ```
9
+
10
+ The prompt remains in the browser. No classification API is called.
examples/browser/index.html ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Veritiana Classifier Example</title></head>
4
+ <body>
5
+ <main>
6
+ <h1>Veritiana Prompt Classifier</h1>
7
+ <textarea id="prompt" rows="8">Refactor this authentication service, preserve API compatibility, migrate the database schema and add rollback tests.</textarea>
8
+ <button id="classify">Classify locally</button>
9
+ <pre id="result">Loading model…</pre>
10
+ </main>
11
+ <script type="module" src="/src/main.js"></script>
12
+ </body>
13
+ </html>
examples/browser/package.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "veritiana-prompt-classifier-browser-example",
3
+ "private": true,
4
+ "version": "1.0.0",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "onnxruntime-web": "1.22.0"
13
+ },
14
+ "devDependencies": {
15
+ "vite": "^7.0.0"
16
+ }
17
+ }
examples/browser/src/main.js ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as ort from "onnxruntime-web";
2
+ import "./style.css";
3
+
4
+ const TASK_LABELS = ["general_chat", "writing", "translation", "summarization", "research", "coding", "mathematics", "document_analysis", "high_stakes"];
5
+ const COMPLEXITY_LABELS = ["low", "medium", "high"];
6
+ const WORD_BINS = 1024;
7
+ const CHAR_BINS = 512;
8
+ const INPUT_SIZE = 1544;
9
+
10
+ function fnv1a(text) {
11
+ let hash = 2166136261;
12
+ for (let i = 0; i < text.length; i += 1) {
13
+ hash ^= text.charCodeAt(i);
14
+ hash = Math.imul(hash, 16777619);
15
+ }
16
+ return hash >>> 0;
17
+ }
18
+
19
+ function buildFeatures(text) {
20
+ const source = String(text || "");
21
+ const lower = source.toLocaleLowerCase();
22
+ const values = new Float32Array(INPUT_SIZE);
23
+ const words = lower.match(/[\p{L}\p{N}_]+/gu) || [];
24
+ const grams = [...words];
25
+ for (let i = 0; i + 1 < words.length; i += 1) grams.push(`${words[i]}_${words[i + 1]}`);
26
+ for (const gram of grams) values[fnv1a(gram) % WORD_BINS] += 1;
27
+
28
+ const compact = lower.replace(/\s+/g, " ");
29
+ for (let i = 0; i + 2 < compact.length; i += 1) {
30
+ values[WORD_BINS + (fnv1a(compact.slice(i, i + 3)) % CHAR_BINS)] += 0.25;
31
+ }
32
+
33
+ const sparseCount = WORD_BINS + CHAR_BINS;
34
+ let norm = 0;
35
+ for (let i = 0; i < sparseCount; i += 1) norm += values[i] * values[i];
36
+ norm = Math.sqrt(norm);
37
+ if (norm > 0) for (let i = 0; i < sparseCount; i += 1) values[i] /= norm;
38
+
39
+ const base = sparseCount;
40
+ values[base] = Math.min(source.length, 4000) / 4000;
41
+ values[base + 1] = Math.min(words.length, 800) / 800;
42
+ values[base + 2] = Math.min((source.match(/\?/g) || []).length, 10) / 10;
43
+ values[base + 3] = Math.min((source.match(/\n/g) || []).length, 30) / 30;
44
+ values[base + 4] = Math.min((source.match(/[{}[\]();=<>/]/g) || []).length, 100) / 100;
45
+ values[base + 5] = /https?:\/\/|www\./i.test(source) ? 1 : 0;
46
+ values[base + 6] = /\b(pdf|document|dokument|file|súbor|attachment|príloha)\b/i.test(source) ? 1 : 0;
47
+ values[base + 7] = 1;
48
+ return values;
49
+ }
50
+
51
+ function argMax(values) {
52
+ let index = 0;
53
+ for (let i = 1; i < values.length; i += 1) if (values[i] > values[index]) index = i;
54
+ return index;
55
+ }
56
+
57
+ const result = document.querySelector("#result");
58
+ const modelUrl = new URL("../../../model.onnx", import.meta.url).href;
59
+ ort.env.wasm.numThreads = 1;
60
+ const session = await ort.InferenceSession.create(modelUrl, { executionProviders: ["wasm"] });
61
+ result.textContent = "Model ready.";
62
+
63
+ document.querySelector("#classify").addEventListener("click", async () => {
64
+ const text = document.querySelector("#prompt").value;
65
+ const features = buildFeatures(text);
66
+ const outputs = await session.run({ features: new ort.Tensor("float32", features, [1, INPUT_SIZE]) });
67
+ const task = Array.from(outputs.task_probabilities.data);
68
+ const complexity = Array.from(outputs.complexity_probabilities.data);
69
+ const taskIndex = argMax(task);
70
+ const complexityIndex = argMax(complexity);
71
+ result.textContent = JSON.stringify({
72
+ task: { label: TASK_LABELS[taskIndex], confidence: task[taskIndex], probabilities: Object.fromEntries(TASK_LABELS.map((label, i) => [label, task[i]])) },
73
+ complexity: { label: COMPLEXITY_LABELS[complexityIndex], confidence: complexity[complexityIndex], probabilities: Object.fromEntries(COMPLEXITY_LABELS.map((label, i) => [label, complexity[i]])) }
74
+ }, null, 2);
75
+ });
examples/browser/src/style.css ADDED
@@ -0,0 +1 @@
 
 
1
+ body{margin:0;background:#06111f;color:#eefbf7;font:16px/1.5 system-ui,sans-serif}main{max-width:850px;margin:60px auto;padding:24px}textarea,pre{box-sizing:border-box;width:100%;border:1px solid #294155;border-radius:12px;background:#0a1928;color:#fff;padding:16px}button{margin:14px 0;padding:12px 18px;border:0;border-radius:9px;background:#24d3a4;color:#06111f;font-weight:800;cursor:pointer}pre{white-space:pre-wrap;overflow:auto}
examples/python/classify.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import json
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import numpy as np
7
+ import onnxruntime as ort
8
+
9
+ REPO_ROOT = Path(__file__).resolve().parents[2]
10
+ sys.path.insert(0, str(REPO_ROOT))
11
+ from features import build_features # noqa: E402
12
+
13
+ TASK_LABELS = [
14
+ "general_chat", "writing", "translation", "summarization", "research",
15
+ "coding", "mathematics", "document_analysis", "high_stakes",
16
+ ]
17
+ COMPLEXITY_LABELS = ["low", "medium", "high"]
18
+
19
+
20
+ def distribution(labels, values):
21
+ return {label: float(value) for label, value in zip(labels, values, strict=True)}
22
+
23
+
24
+ def classify(text: str) -> dict:
25
+ model_path = REPO_ROOT / "model.onnx"
26
+ session = ort.InferenceSession(str(model_path), providers=["CPUExecutionProvider"])
27
+ features = build_features(text).astype(np.float32).reshape(1, -1)
28
+ outputs = session.run(None, {"features": features})
29
+ task_probs = outputs[0][0]
30
+ complexity_probs = outputs[1][0]
31
+ task_index = int(np.argmax(task_probs))
32
+ complexity_index = int(np.argmax(complexity_probs))
33
+ return {
34
+ "task": {
35
+ "label": TASK_LABELS[task_index],
36
+ "confidence": float(task_probs[task_index]),
37
+ "probabilities": distribution(TASK_LABELS, task_probs),
38
+ },
39
+ "complexity": {
40
+ "label": COMPLEXITY_LABELS[complexity_index],
41
+ "confidence": float(complexity_probs[complexity_index]),
42
+ "probabilities": distribution(COMPLEXITY_LABELS, complexity_probs),
43
+ },
44
+ }
45
+
46
+
47
+ def main() -> None:
48
+ if len(sys.argv) < 2:
49
+ raise SystemExit('Usage: python examples/python/classify.py "your prompt"')
50
+ print(json.dumps(classify(" ".join(sys.argv[1:])), indent=2))
51
+
52
+
53
+ if __name__ == "__main__":
54
+ main()
features.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """AI Meter v1.4.9-compatible 1544-dimensional feature extraction."""
3
+ from __future__ import annotations
4
+ import re
5
+ from typing import Iterable
6
+ import numpy as np
7
+ import regex
8
+
9
+ WORD_BINS = 1024
10
+ CHAR_BINS = 512
11
+ NUMERIC_FEATURES = 8
12
+ INPUT_SIZE = WORD_BINS + CHAR_BINS + NUMERIC_FEATURES
13
+
14
+ TECH_RE = re.compile(r"[{}\[\]();=<>/]")
15
+ URL_RE = re.compile(r"https?://|www\.", re.I)
16
+ DOC_RE = re.compile(r"\b(pdf|document|dokument|file|súbor|attachment|príloha)\b", re.I)
17
+
18
+ def _utf16_units(text: str) -> Iterable[int]:
19
+ raw = text.encode("utf-16-le", errors="surrogatepass")
20
+ for index in range(0, len(raw), 2):
21
+ yield raw[index] | (raw[index + 1] << 8)
22
+
23
+ def fnv1a_js(text: str) -> int:
24
+ """32-bit FNV-1a over JavaScript-compatible UTF-16 code units."""
25
+ value = 2166136261
26
+ for unit in _utf16_units(text):
27
+ value ^= unit
28
+ value = (value * 16777619) & 0xFFFFFFFF
29
+ return value
30
+
31
+ def build_features(text: str) -> np.ndarray:
32
+ source = str(text or "")
33
+ lower = source.lower()
34
+ values = np.zeros(INPUT_SIZE, dtype=np.float32)
35
+ words = regex.findall(r"[\p{L}\p{N}_]+", lower)
36
+ grams = list(words)
37
+ grams.extend(f"{words[i]}_{words[i + 1]}" for i in range(len(words) - 1))
38
+ for gram in grams:
39
+ values[fnv1a_js(gram) % WORD_BINS] += 1.0
40
+ compact = regex.sub(r"\s+", " ", lower)
41
+ for index in range(max(0, len(compact) - 2)):
42
+ trigram = compact[index:index + 3]
43
+ values[WORD_BINS + (fnv1a_js(trigram) % CHAR_BINS)] += 0.25
44
+ sparse = values[:WORD_BINS + CHAR_BINS]
45
+ norm = float(np.linalg.norm(sparse))
46
+ if norm > 0:
47
+ sparse /= norm
48
+ base = WORD_BINS + CHAR_BINS
49
+ values[base] = min(len(source), 4000) / 4000
50
+ values[base + 1] = min(len(words), 800) / 800
51
+ values[base + 2] = min(source.count("?"), 10) / 10
52
+ values[base + 3] = min(source.count("\n"), 30) / 30
53
+ values[base + 4] = min(len(TECH_RE.findall(source)), 100) / 100
54
+ values[base + 5] = 1.0 if URL_RE.search(source) else 0.0
55
+ values[base + 6] = 1.0 if DOC_RE.search(source) else 0.0
56
+ values[base + 7] = 1.0
57
+ return values
model.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:dca7560742f19207d089d469c2907e7a1b7c06e2bbf14d2404a5d9359043f572
3
+ size 75346
prepare_training_set.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ from __future__ import annotations
3
+ import argparse, json, random, re
4
+ from collections import Counter, defaultdict
5
+ from pathlib import Path
6
+
7
+ TASKS=['general_chat','writing','translation','summarization','research','coding','mathematics','document_analysis','high_stakes']
8
+ POLITE_PREFIXES=['Please ','Could you ','I need you to ','Help me to ']
9
+
10
+ def normalize(s): return re.sub(r'\s+',' ',s).strip()
11
+ def variants(text):
12
+ base=normalize(text)
13
+ out=[]
14
+ if base:
15
+ out.append(base)
16
+ if base[0].islower(): out.append(base[0].upper()+base[1:])
17
+ if not base.endswith(('?','.','!')): out.append(base+'.')
18
+ for p in POLITE_PREFIXES:
19
+ if not base.lower().startswith(('please ','could you ','i need you to ','help me to ')):
20
+ out.append(p+base[0].lower()+base[1:])
21
+ seen=[]
22
+ for x in out:
23
+ if x not in seen: seen.append(x)
24
+ return seen
25
+
26
+ def main():
27
+ ap=argparse.ArgumentParser()
28
+ ap.add_argument('input',type=Path)
29
+ ap.add_argument('--output',type=Path,default=Path('data/processed/training-balanced.jsonl'))
30
+ ap.add_argument('--report',type=Path,default=Path('data/processed/training-balanced-report.json'))
31
+ ap.add_argument('--seed',type=int,default=42)
32
+ ap.add_argument('--confidence',type=float,default=.65)
33
+ ap.add_argument('--target-per-task',type=int,default=800)
34
+ ap.add_argument('--max-per-task',type=int,default=2000)
35
+ args=ap.parse_args(); rng=random.Random(args.seed)
36
+ rows=[json.loads(x) for x in args.input.read_text(encoding='utf-8').splitlines() if x.strip()]
37
+ accepted=[]; rejected=[]
38
+ for r in rows:
39
+ fixed=r.get('label_method') in {'source_fixed','human_override'}
40
+ if fixed or float(r.get('label_confidence',0))>=args.confidence:
41
+ accepted.append(r)
42
+ else: rejected.append(r)
43
+ groups=defaultdict(list)
44
+ for r in accepted: groups[r['task']].append(r)
45
+ output=[]; augmented=Counter()
46
+ for task in TASKS:
47
+ group=groups[task]
48
+ rng.shuffle(group)
49
+ selected=group[:args.max_per_task]
50
+ output.extend(selected)
51
+ needed=max(0,args.target_per_task-len(selected))
52
+ if needed and selected:
53
+ pool=[]
54
+ for r in selected:
55
+ for i,v in enumerate(variants(r['text'])[1:],1):
56
+ n=dict(r); n['id']=f"{r['id']}:aug{i}"; n['text']=v
57
+ n['label_method']='deterministic_augmentation'; n['derived_from']=r['id']; n['label_confidence']=r.get('label_confidence',1.0)
58
+ pool.append(n)
59
+ rng.shuffle(pool)
60
+ take=pool[:needed]
61
+ output.extend(take); augmented[task]+=len(take)
62
+ rng.shuffle(output)
63
+ args.output.parent.mkdir(parents=True,exist_ok=True)
64
+ with args.output.open('w',encoding='utf-8') as f:
65
+ for r in output: f.write(json.dumps(r,ensure_ascii=False)+'\n')
66
+ report={
67
+ 'input_rows':len(rows),'accepted_rows':len(accepted),'rejected_low_confidence':len(rejected),
68
+ 'output_rows':len(output),'confidence_threshold':args.confidence,
69
+ 'by_task':dict(Counter(r['task'] for r in output)),
70
+ 'by_complexity':dict(Counter(r['complexity'] for r in output)),
71
+ 'by_label_method':dict(Counter(r.get('label_method','unknown') for r in output)),
72
+ 'augmented_by_task':dict(augmented),
73
+ 'warning':'Augmented rows are lexical variants, not independent human examples.'
74
+ }
75
+ args.report.write_text(json.dumps(report,indent=2),encoding='utf-8')
76
+ print(json.dumps(report,indent=2))
77
+ if __name__=='__main__': main()
requirements-inference.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ numpy==2.3.5
2
+ onnxruntime>=1.22,<2
3
+ regex==2026.5.9
requirements-training.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ numpy==2.3.5
2
+ scikit-learn==1.8.0
3
+ regex==2026.5.9
4
+ onnx>=1.17,<2
requirements-upload.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ huggingface_hub>=0.34,<1
train.py ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Reproduce Veritiana AI Meter classifier v3.1.0 from training-balanced.jsonl."""
3
+ from __future__ import annotations
4
+ import argparse, hashlib, json, struct, sys, time
5
+ from collections import Counter
6
+ from pathlib import Path
7
+ import numpy as np
8
+ from sklearn.linear_model import LogisticRegression
9
+ from sklearn.metrics import classification_report, confusion_matrix
10
+ from sklearn.model_selection import GroupShuffleSplit
11
+ from features import INPUT_SIZE, build_features
12
+
13
+ TASK_LABELS = ["general_chat","writing","translation","summarization","research","coding","mathematics","document_analysis","high_stakes"]
14
+ COMPLEXITY_LABELS = ["low","medium","high"]
15
+
16
+ def sha256(path: Path) -> str:
17
+ return hashlib.sha256(path.read_bytes()).hexdigest()
18
+
19
+ def _varint(number: int) -> bytes:
20
+ number = int(number)
21
+ if number < 0: number = (1 << 64) + number
22
+ output = bytearray()
23
+ while True:
24
+ byte = number & 0x7F; number >>= 7
25
+ output.append(byte | (0x80 if number else 0))
26
+ if not number: return bytes(output)
27
+ def _key(field: int, wire: int) -> bytes: return _varint((field << 3) | wire)
28
+ def _fv(field: int, value: int) -> bytes: return _key(field,0)+_varint(value)
29
+ def _fb(field: int, value: bytes) -> bytes: return _key(field,2)+_varint(len(value))+value
30
+ def _fs(field: int, value: str) -> bytes: return _fb(field,value.encode())
31
+ def _msg(field: int, value: bytes) -> bytes: return _fb(field,value)
32
+ def _packed(field: int, values) -> bytes: return _fb(field,b''.join(_varint(v) for v in values))
33
+ def _tensor(name: str, array: np.ndarray) -> bytes:
34
+ array=np.ascontiguousarray(array.astype(np.float32))
35
+ return _packed(1,array.shape)+_fv(2,1)+_fs(8,name)+_fb(9,array.tobytes(order='C'))
36
+ def _dim(value): return _fs(2,value) if isinstance(value,str) else _fv(1,value)
37
+ def _shape(dims): return b''.join(_msg(1,_dim(value)) for value in dims)
38
+ def _tensor_type(element,dims): return _fv(1,element)+_msg(2,_shape(dims))
39
+ def _type_proto(element,dims): return _msg(1,_tensor_type(element,dims))
40
+ def _value_info(name,dims): return _fs(1,name)+_msg(2,_type_proto(1,dims))
41
+ def _attr_int(name,value): return _fs(1,name)+_fv(3,value)+_fv(20,2)
42
+ def _node(inputs,outputs,operation,name='',attributes=None):
43
+ value=b''.join(_fs(1,item) for item in inputs)+b''.join(_fs(2,item) for item in outputs)
44
+ if name: value += _fs(3,name)
45
+ value += _fs(4,operation)
46
+ for attribute in attributes or []: value += _msg(5,attribute)
47
+ return value
48
+ def _graph(nodes,initializers,inputs,outputs,name):
49
+ return (b''.join(_msg(1,node) for node in nodes)+_fs(2,name)+
50
+ b''.join(_msg(5,item) for item in initializers)+
51
+ b''.join(_msg(11,item) for item in inputs)+
52
+ b''.join(_msg(12,item) for item in outputs))
53
+ def _opset(version): return _fv(2,version)
54
+ def _kv(key,value): return _fs(1,key)+_fs(2,value)
55
+
56
+ def export_onnx(task_model, complexity_model, destination: Path, version: str) -> None:
57
+ initializers=[
58
+ _tensor('task_weights',task_model.coef_.astype(np.float32).T.copy()),
59
+ _tensor('task_bias',task_model.intercept_.astype(np.float32).copy()),
60
+ _tensor('complexity_weights',complexity_model.coef_.astype(np.float32).T.copy()),
61
+ _tensor('complexity_bias',complexity_model.intercept_.astype(np.float32).copy()),
62
+ ]
63
+ nodes=[
64
+ _node(['features','task_weights'],['task_mm'],'MatMul','TaskMatMul'),
65
+ _node(['task_mm','task_bias'],['task_logits'],'Add','TaskAdd'),
66
+ _node(['task_logits'],['task_probabilities'],'Softmax','TaskSoftmax',[_attr_int('axis',1)]),
67
+ _node(['features','complexity_weights'],['complexity_mm'],'MatMul','ComplexityMatMul'),
68
+ _node(['complexity_mm','complexity_bias'],['complexity_logits'],'Add','ComplexityAdd'),
69
+ _node(['complexity_logits'],['complexity_probabilities'],'Softmax','ComplexitySoftmax',[_attr_int('axis',1)]),
70
+ ]
71
+ graph=_graph(nodes,initializers,[_value_info('features',['batch',1544])],
72
+ [_value_info('task_probabilities',['batch',9]),_value_info('complexity_probabilities',['batch',3])],
73
+ 'VeritianaAIMeterClassifier')
74
+ metadata={
75
+ 'model':'Veritiana Multilingual Intent Classifier','version':version,'input_size':'1544',
76
+ 'task_labels':json.dumps(TASK_LABELS),'complexity_labels':json.dumps(COMPLEXITY_LABELS),
77
+ 'training_data':'OASST1 + CoEdIT + MBPP+ + IFEval; filtered, weak-labeled, balanced',
78
+ 'feature_contract':'AI Meter v1.4.9 compatible'
79
+ }
80
+ payload=(_fv(1,8)+_fs(2,'veritiana-ai-meter-model-trainer')+_fs(3,version)+
81
+ _msg(7,graph)+_msg(8,_opset(13))+b''.join(_msg(14,_kv(k,v)) for k,v in metadata.items()))
82
+ destination.write_bytes(payload)
83
+
84
+ def main() -> int:
85
+ parser=argparse.ArgumentParser()
86
+ parser.add_argument('dataset',type=Path,help='Prepared training-balanced.jsonl')
87
+ parser.add_argument('--output-dir',type=Path,default=Path('output'))
88
+ parser.add_argument('--version',default='3.1.0-multisource-balanced')
89
+ parser.add_argument('--test-size',type=float,default=0.20)
90
+ parser.add_argument('--seed',type=int,default=42)
91
+ parser.add_argument('--c',type=float,default=4.0)
92
+ args=parser.parse_args()
93
+ rows=[json.loads(line) for line in args.dataset.read_text(encoding='utf-8').splitlines() if line.strip()]
94
+ if not rows: raise ValueError('Empty dataset')
95
+ task_map={label:index for index,label in enumerate(TASK_LABELS)}
96
+ complexity_map={label:index for index,label in enumerate(COMPLEXITY_LABELS)}
97
+ X=np.vstack([build_features(row['text']) for row in rows]).astype(np.float32)
98
+ task_y=np.asarray([task_map[row['task']] for row in rows],dtype=np.int64)
99
+ complexity_y=np.asarray([complexity_map[row['complexity']] for row in rows],dtype=np.int64)
100
+ groups=[row.get('derived_from') or row.get('normalized_hash') or row.get('id') for row in rows]
101
+ splitter=GroupShuffleSplit(n_splits=1,test_size=args.test_size,random_state=args.seed)
102
+ train_index,test_index=next(splitter.split(X,task_y,groups=groups))
103
+ def fit(target):
104
+ model=LogisticRegression(C=args.c,max_iter=3000,solver='lbfgs',class_weight='balanced',random_state=args.seed)
105
+ model.fit(X[train_index],target[train_index]); return model
106
+ started=time.perf_counter(); task_model=fit(task_y); complexity_model=fit(complexity_y); seconds=time.perf_counter()-started
107
+ task_prediction=task_model.predict(X[test_index]); complexity_prediction=complexity_model.predict(X[test_index])
108
+ task_report=classification_report(task_y[test_index],task_prediction,labels=list(range(9)),target_names=TASK_LABELS,output_dict=True,zero_division=0)
109
+ complexity_report=classification_report(complexity_y[test_index],complexity_prediction,labels=list(range(3)),target_names=COMPLEXITY_LABELS,output_dict=True,zero_division=0)
110
+ evaluation={
111
+ 'summary':{'task_accuracy':task_report['accuracy'],'task_macro_f1':task_report['macro avg']['f1-score'],
112
+ 'complexity_accuracy':complexity_report['accuracy'],'complexity_macro_f1':complexity_report['macro avg']['f1-score'],
113
+ 'training_seconds':seconds},
114
+ 'task_report':task_report,'complexity_report':complexity_report,
115
+ 'task_confusion_matrix':confusion_matrix(task_y[test_index],task_prediction,labels=list(range(9))).tolist(),
116
+ 'complexity_confusion_matrix':confusion_matrix(complexity_y[test_index],complexity_prediction,labels=list(range(3))).tolist()
117
+ }
118
+ args.output_dir.mkdir(parents=True,exist_ok=True)
119
+ model_path=args.output_dir/'veritiana-classifier-v2.onnx'; export_onnx(task_model,complexity_model,model_path,args.version)
120
+ (args.output_dir/'evaluation.json').write_text(json.dumps(evaluation,ensure_ascii=False,indent=2),encoding='utf-8')
121
+ meta={
122
+ 'model':'Veritiana Multilingual Intent Classifier','version':args.version,'engine':'ONNX Runtime Web',
123
+ 'architecture':'1544 hashed lexical/character/numeric features; dual multinomial logistic heads',
124
+ 'input':{'name':'features','dtype':'float32','shape':['batch',1544]},
125
+ 'outputs':[{'name':'task_probabilities','dtype':'float32','shape':['batch',9]},
126
+ {'name':'complexity_probabilities','dtype':'float32','shape':['batch',3]}],
127
+ 'task_labels':TASK_LABELS,'complexity_labels':COMPLEXITY_LABELS,
128
+ 'training_data':{'dataset_sha256':sha256(args.dataset),'balanced_rows':len(rows),'train_rows':len(train_index),'test_rows':len(test_index),
129
+ 'label_method_counts':dict(Counter(row.get('label_method','unknown') for row in rows))},
130
+ 'validation':evaluation['summary'],'onnx_sha256':sha256(model_path),
131
+ 'limitations':'Validation uses weak labels and deterministic augmentations; it is not an independent human-ground-truth benchmark.'
132
+ }
133
+ (args.output_dir/'classifier-meta.json').write_text(json.dumps(meta,ensure_ascii=False,indent=2),encoding='utf-8')
134
+ print(json.dumps({'model':str(model_path),'onnx_sha256':sha256(model_path),'evaluation':evaluation['summary']},indent=2))
135
+ return 0
136
+ if __name__=='__main__': raise SystemExit(main())
training-config.json ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "release_version": "3.1.0-multisource-balanced",
3
+ "random_seed": 42,
4
+ "test_size": 0.2,
5
+ "splitter": "GroupShuffleSplit",
6
+ "group_key_priority": [
7
+ "derived_from",
8
+ "normalized_hash",
9
+ "id"
10
+ ],
11
+ "classifier": {
12
+ "type": "sklearn.linear_model.LogisticRegression",
13
+ "solver": "lbfgs",
14
+ "C": 4.0,
15
+ "max_iter": 3000,
16
+ "class_weight": "balanced"
17
+ },
18
+ "preparation": {
19
+ "confidence_threshold": 0.65,
20
+ "target_per_task": 800,
21
+ "max_per_task": 2000,
22
+ "accepted_label_methods": [
23
+ "source_fixed",
24
+ "human_override"
25
+ ],
26
+ "augmentation": "deterministic lexical variants"
27
+ },
28
+ "features": {
29
+ "input_size": 1544,
30
+ "word_bigram_bins": 1024,
31
+ "character_trigram_bins": 512,
32
+ "numeric_features": 8,
33
+ "hash": "FNV-1a 32-bit over JavaScript UTF-16 code units"
34
+ },
35
+ "labels": {
36
+ "task": [
37
+ "general_chat",
38
+ "writing",
39
+ "translation",
40
+ "summarization",
41
+ "research",
42
+ "coding",
43
+ "mathematics",
44
+ "document_analysis",
45
+ "high_stakes"
46
+ ],
47
+ "complexity": [
48
+ "low",
49
+ "medium",
50
+ "high"
51
+ ]
52
+ },
53
+ "reference_environment": {
54
+ "python": "3.13.5",
55
+ "numpy": "2.3.5",
56
+ "scikit-learn": "1.8.0",
57
+ "regex": "2026.5.9",
58
+ "onnx_opset": 13,
59
+ "onnx_ir_version": 8
60
+ }
61
+ }
upload_to_hub.py ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ from pathlib import Path
4
+ from huggingface_hub import HfApi
5
+
6
+
7
+ def main() -> None:
8
+ parser = argparse.ArgumentParser(description="Upload the Veritiana classifier repository to Hugging Face Hub.")
9
+ parser.add_argument("repo_id", help="Target repository, for example Veritiana-AI/prompt-task-complexity-classifier")
10
+ parser.add_argument("--private", action="store_true", help="Create the repository as private")
11
+ args = parser.parse_args()
12
+
13
+ root = Path(__file__).resolve().parent
14
+ api = HfApi()
15
+ api.create_repo(repo_id=args.repo_id, repo_type="model", private=args.private, exist_ok=True)
16
+ api.upload_folder(
17
+ repo_id=args.repo_id,
18
+ repo_type="model",
19
+ folder_path=str(root),
20
+ ignore_patterns=["*.zip", "__pycache__/*", ".venv/*", "node_modules/*", "dist/*"],
21
+ commit_message="Publish Veritiana Prompt Task & Complexity Classifier v3.1.0",
22
+ )
23
+ print(f"Uploaded to https://huggingface.co/{args.repo_id}")
24
+
25
+
26
+ if __name__ == "__main__":
27
+ main()