TwilightTechie commited on
Commit
f0e7192
·
verified ·
1 Parent(s): 06b8543

Publish experimental commitment-classifier pilot

Browse files
Files changed (37) hide show
  1. .gitattributes +1 -0
  2. LICENSE +17 -0
  3. README.md +152 -0
  4. UPSTREAM_LICENSES.md +27 -0
  5. config.json +63 -0
  6. label-contract.json +11 -0
  7. model.safetensors +3 -0
  8. reproducibility/LICENSE +201 -0
  9. reproducibility/README.md +37 -0
  10. reproducibility/environment.txt +64 -0
  11. reproducibility/sequence_repair/__init__.py +1 -0
  12. reproducibility/sequence_repair/complete_adapter.py +94 -0
  13. reproducibility/sequence_repair/context.py +85 -0
  14. reproducibility/sequence_repair/corpus.py +55 -0
  15. reproducibility/sequence_repair/data.py +107 -0
  16. reproducibility/sequence_repair/evaluate.py +147 -0
  17. reproducibility/sequence_repair/export.py +172 -0
  18. reproducibility/sequence_repair/fetch_sources.py +48 -0
  19. reproducibility/sequence_repair/hydrate_annotations.py +95 -0
  20. reproducibility/sequence_repair/initialize.py +76 -0
  21. reproducibility/sequence_repair/model.py +94 -0
  22. reproducibility/sequence_repair/optimization.py +220 -0
  23. reproducibility/sequence_repair/prepare_context.py +158 -0
  24. reproducibility/sequence_repair/prepare_retention.py +87 -0
  25. reproducibility/sequence_repair/retention.py +410 -0
  26. reproducibility/sequence_repair/runtime_mapping.py +58 -0
  27. reproducibility/sequence_repair/selection.py +158 -0
  28. reproducibility/sequence_repair/task_head.py +102 -0
  29. reproducibility/sequence_repair/train.py +791 -0
  30. reproducibility/sequence_repair/training_order.py +134 -0
  31. reproducibility/source-files.json +22 -0
  32. reproducibility/training.json +26 -0
  33. special_tokens_map.json +55 -0
  34. task-head.json +49 -0
  35. tokenizer.json +3 -0
  36. tokenizer_config.json +2018 -0
  37. training-origin.json +5 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* 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
 
 
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
36
+ tokenizer.json filter=lfs diff=lfs merge=lfs -text
LICENSE ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
5
+ associated documentation files (the "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the
8
+ following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial
11
+ portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
14
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
15
+ EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
16
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
17
+ USE OR OTHER DEALINGS IN THE SOFTWARE.
README.md ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ language:
4
+ - en
5
+ library_name: transformers
6
+ pipeline_tag: text-classification
7
+ base_model: vllm-sr/Vela-1.0-Encoder-307M
8
+ base_model_relation: finetune
9
+ datasets:
10
+ - TwilightTechie/commitment-statements-pilot
11
+ tags:
12
+ - commitment-detection
13
+ - decision-detection
14
+ - experimental
15
+ - vela
16
+ widget:
17
+ - text: We have decided to update the document today.
18
+ - text: I like mountains.
19
+ ---
20
+
21
+ # Vela commitment classifier — experimental pilot
22
+
23
+ A short English statement classifier that detects an **expressed decision, firm
24
+ intention, or commitment**. This is a personal fine-tuning experiment derived from
25
+ Vela, not an official vLLM Semantic Router release or an endorsed product.
26
+
27
+ | Label | Meaning |
28
+ | --- | --- |
29
+ | `DECISION_EXPRESSED` | An affirmed chosen action, firm intention, commitment, explicit rejection or chosen inaction. Explicitly reported past choices also count. |
30
+ | `NO_DECISION_EXPRESSED` | Preferences, questions, requests, possibilities, predictions, example quotations, or completed-action reports without an expressed choice. |
31
+
32
+ It does not determine whether a promise was fulfilled, infer an unspoken mental
33
+ state, extract tasks, or decide whether a statement deserves a decision-log entry.
34
+ Routine firm process commitments count as positive under this broad contract.
35
+
36
+ ## Quick start
37
+
38
+ ```bash
39
+ pip install torch transformers==4.57.6
40
+ ```
41
+
42
+ ```python
43
+ from transformers import pipeline
44
+
45
+ classifier = pipeline(
46
+ "text-classification",
47
+ model="TwilightTechie/vela-commitment-classifier-307m",
48
+ tokenizer="TwilightTechie/vela-commitment-classifier-307m",
49
+ device=-1, # CPU
50
+ )
51
+ print(classifier("We have decided to update the document today."))
52
+ print(classifier("I like mountains."))
53
+ ```
54
+
55
+ The first example was classified as `DECISION_EXPRESSED`; the second as
56
+ `NO_DECISION_EXPRESSED`. A GPU or hosted endpoint is not required for inference.
57
+ Scores are model softmax outputs, **not calibrated certainty**. Run only trusted
58
+ standard library code; this model requires no `trust_remote_code=True`.
59
+
60
+ Keep inputs to **256 tokens including special tokens**. You can enforce this
61
+ before classification:
62
+
63
+ ```python
64
+ text = "We agreed to postpone the rollout until the pilot finishes."
65
+ if len(classifier.tokenizer(text)["input_ids"]) > 256:
66
+ raise ValueError("This pilot's input budget is 256 tokens")
67
+ print(classifier(text))
68
+ ```
69
+
70
+ The foundation has a larger architectural context capacity; this fine-tuned
71
+ pilot has not established quality on long documents or conversations.
72
+
73
+ ## Training and provenance
74
+
75
+ - Foundation: [`vllm-sr/Vela-1.0-Encoder-307M`](https://huggingface.co/vllm-sr/Vela-1.0-Encoder-307M).
76
+ - Exact foundation revision: `720ab37904ce15054068d429381bfae7549f00f6`.
77
+ - Architecture: ModernBERT sequence classifier with a fresh two-label task head.
78
+ - Method: complete checkpoint, updating the final two encoder layers and task
79
+ head; other encoder parameters stayed frozen and passed preservation checks.
80
+ - Trainable parameters: 10,622,210.
81
+ - Training data: 118 short, synthetic English examples. Development data: 14.
82
+ Assistant-authored examples were accepted by the user for this pilot; this
83
+ does not constitute independent expert annotation. Original row review and
84
+ provenance fields remain unchanged in the dataset.
85
+ - 100 optimizer steps, seed 42, batch size 2, gradient accumulation 2.
86
+ - Encoder learning rate 0.00002; head learning rate 0.0001; maximum length 256.
87
+ - BF16 forward autocast, FP32 parameters; FP32 development evaluation.
88
+ - Hardware: one NVIDIA L4 on Modal; peak allocated training memory 1.3697 GiB.
89
+ - Selection: development macro F1 at steps 0, 25, 50, 75, 100. Step 75 selected.
90
+ - Remote environment: Python 3.12, Torch 2.8.0+cu128, Transformers 4.57.6,
91
+ PEFT 0.18.1, Accelerate 1.10.1, scikit-learn 1.7.2.
92
+
93
+ The training recipe and environment are included under `reproducibility/`.
94
+ The training code comes from the public
95
+ [vLLM Semantic Router repository](https://github.com/vllm-project/semantic-router).
96
+ `source-files.json` captures hashes of the exact trainer modules used; the
97
+ experiment was made in a local checkout, not claimed as an upstream release.
98
+ No private source document or credentials are included.
99
+
100
+ ## Development results — not independent testing
101
+
102
+ All rows below use the same **14-example development set**:
103
+
104
+ | Classifier | Correct | Macro F1 |
105
+ | --- | --- | --- |
106
+ | Fixed keyword rule | 12/14 | 0.8542 |
107
+ | TF-IDF + logistic regression | 13/14 | 0.9282 |
108
+ | Decision 2.0 Kai, fixed yes/no question | 11/14 | 0.7754 |
109
+ | This selected Vela checkpoint | 14/14 | 1.0000 |
110
+
111
+ The neural checkpoint was selected using this development set. Additional
112
+ training examples were authored after inspecting earlier development errors.
113
+ Therefore, **14/14 does not imply general accuracy or enterprise readiness**.
114
+ FP32 GPU and local CPU reload produced the same class predictions; maximum
115
+ probability difference was about 0.00000185. This is artifact verification,
116
+ not independent quality evaluation.
117
+
118
+ A 16-example same-author synthetic test set remains unpublished and unevaluated.
119
+ A fresh, realistic, independently annotated evaluation set is still needed.
120
+
121
+ ## Intended use and limitations
122
+
123
+ Use this for learning, local experimentation, and a candidate classifier in
124
+ human-reviewed workflows. It has not been qualified for automatic authoritative
125
+ logging, production routing, or consequential decisions.
126
+
127
+ - English-only evaluated scope despite the multilingual foundation.
128
+ - Very small authored dataset; wording and topic bias are likely.
129
+ - Standalone short statements only; multi-turn reference resolution is not tested.
130
+ - Conditional, canceled, sarcastic and context-dependent statements need a
131
+ separately reviewed contract; predictions may be unreliable there.
132
+ - A past decision may be superseded; this classifier does not identify current
133
+ policy, author authority, or supersession links.
134
+ - High scores can be overconfident. Calibration and realistic false-positive
135
+ rates have not been measured.
136
+ - Transformers 4.57.6 emitted a known class of Mistral regex warnings when loading
137
+ this non-Mistral tokenizer locally. No tokenizer patch was applied. Basic
138
+ reload checks passed; a broader tokenizer audit has not been performed.
139
+
140
+ ## License and attribution
141
+
142
+ The pinned Vela model card declares MIT; it identifies
143
+ [`jhu-clsp/mmBERT-base`](https://huggingface.co/jhu-clsp/mmBERT-base) as its upstream
144
+ foundation, whose model card also declares MIT. The fine-tuned model is shared
145
+ under MIT with those upstream declarations retained. See `LICENSE` and
146
+ `UPSTREAM_LICENSES.md` for the recorded absence of separate upstream notice files.
147
+ No upstream copyright owner or year has been invented.
148
+
149
+ The reproducibility code and synthetic dataset are distributed separately under
150
+ Apache-2.0, following the Semantic Router repository's code/data context. See
151
+ `reproducibility/LICENSE` and the dataset card. This model license does not
152
+ relicense upstream training corpora or unrelated dependencies.
UPSTREAM_LICENSES.md ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Upstream license declarations and provenance
2
+
3
+ This distribution contains a fine-tuned derivative of
4
+ [`vllm-sr/Vela-1.0-Encoder-307M`](https://huggingface.co/vllm-sr/Vela-1.0-Encoder-307M),
5
+ using revision `720ab37904ce15054068d429381bfae7549f00f6`.
6
+ That revision's model card declares `license: mit`. Its upstream model is
7
+ [`jhu-clsp/mmBERT-base`](https://huggingface.co/jhu-clsp/mmBERT-base), whose queried
8
+ model card also declares MIT.
9
+
10
+ The queried pinned Vela files and upstream mmBERT files provide no separate
11
+ LICENSE or NOTICE file. This omission is recorded rather than manufacturing
12
+ an upstream copyright notice or assigning ownership of their weights to the
13
+ fine-tuning author. The model license file contains the standard MIT permission
14
+ and warranty terms, sourced from the
15
+ [SPDX MIT license text](https://github.com/spdx/license-list-data/blob/main/text/MIT.txt).
16
+ Any authoritative upstream copyright notices subsequently supplied should be
17
+ preserved with this distribution. These statements reflect published license
18
+ declarations rather than a comprehensive audit of upstream training corpora.
19
+
20
+ The new classifier head and final two encoder layers were updated in a personal
21
+ training experiment. This derivative is not an official upstream release and
22
+ does not imply endorsement by vLLM Semantic Router or JHU.
23
+
24
+ The accompanying reproduction source and synthetic dataset retain Apache-2.0
25
+ licensing from the Semantic Router experiment's source/data context. Their
26
+ license copies are separate from the MIT model license. No private reference
27
+ document, original test examples or credentials are distributed.
config.json ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architectures": [
3
+ "ModernBertForSequenceClassification"
4
+ ],
5
+ "attention_bias": false,
6
+ "attention_dropout": 0.0,
7
+ "bos_token_id": 2,
8
+ "classifier_activation": "gelu",
9
+ "classifier_bias": false,
10
+ "classifier_dropout": 0.0,
11
+ "classifier_pooling": "mean",
12
+ "cls_token_id": 1,
13
+ "decoder_bias": true,
14
+ "deterministic_flash_attn": false,
15
+ "dtype": "float32",
16
+ "embedding_dropout": 0.0,
17
+ "eos_token_id": 1,
18
+ "global_attn_every_n_layers": 3,
19
+ "global_rope_theta": 160000,
20
+ "gradient_checkpointing": false,
21
+ "hidden_activation": "gelu",
22
+ "hidden_size": 768,
23
+ "id2label": {
24
+ "0": "NO_DECISION_EXPRESSED",
25
+ "1": "DECISION_EXPRESSED"
26
+ },
27
+ "initializer_cutoff_factor": 2.0,
28
+ "initializer_range": 0.02,
29
+ "intermediate_size": 1152,
30
+ "label2id": {
31
+ "DECISION_EXPRESSED": 1,
32
+ "NO_DECISION_EXPRESSED": 0
33
+ },
34
+ "layer_norm_eps": 1e-05,
35
+ "local_attention": 128,
36
+ "local_rope_theta": 160000,
37
+ "mask_token_id": 4,
38
+ "max_position_embeddings": 32768,
39
+ "mlp_bias": false,
40
+ "mlp_dropout": 0.0,
41
+ "model_type": "modernbert",
42
+ "norm_bias": false,
43
+ "norm_eps": 1e-05,
44
+ "num_attention_heads": 12,
45
+ "num_hidden_layers": 22,
46
+ "pad_token_id": 0,
47
+ "position_embedding_type": "sans_pos",
48
+ "problem_type": "single_label_classification",
49
+ "repad_logits_with_grad": false,
50
+ "rope_scaling": {
51
+ "beta_fast": 32.0,
52
+ "beta_slow": 1.0,
53
+ "factor": 4.0,
54
+ "original_max_position_embeddings": 8192,
55
+ "rope_type": "yarn",
56
+ "truncate": true
57
+ },
58
+ "sep_token_id": 1,
59
+ "sparse_pred_ignore_index": -100,
60
+ "sparse_prediction": false,
61
+ "transformers_version": "4.57.6",
62
+ "vocab_size": 256000
63
+ }
label-contract.json ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "problem_type": "single_label_classification",
3
+ "label2id": {
4
+ "NO_DECISION_EXPRESSED": 0,
5
+ "DECISION_EXPRESSED": 1
6
+ },
7
+ "id2label": {
8
+ "0": "NO_DECISION_EXPRESSED",
9
+ "1": "DECISION_EXPRESSED"
10
+ }
11
+ }
model.safetensors ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:fcadfb68f0ea218ca6b015a9517af234049e3953cb4376d2c2ffc3803917dcb4
3
+ size 1230141424
reproducibility/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.
reproducibility/README.md ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Reproduce the pilot
2
+
3
+ Use a Linux GPU environment matching `environment.txt`. This is the exact
4
+ training source; dependency versions and data hashes are in `training.json`.
5
+ Hardware and numerical differences can change the selected checkpoint.
6
+
7
+ Download the foundation at its pinned revision and this release's synthetic
8
+ train/validation data. Run from this directory; choose a new output directory.
9
+
10
+ ```python
11
+ from huggingface_hub import snapshot_download
12
+
13
+ snapshot_download(
14
+ "vllm-sr/Vela-1.0-Encoder-307M",
15
+ revision="720ab37904ce15054068d429381bfae7549f00f6",
16
+ local_dir="base",
17
+ )
18
+ snapshot_download(
19
+ "TwilightTechie/commitment-statements-pilot", repo_type="dataset", local_dir="dataset"
20
+ )
21
+ ```
22
+
23
+ ```bash
24
+ python -m sequence_repair.train \
25
+ --method full --fresh-head --trainable-last-layers 2 \
26
+ --base base --base-id vllm-sr/Vela-1.0-Encoder-307M \
27
+ --base-revision 720ab37904ce15054068d429381bfae7549f00f6 \
28
+ --contract ../label-contract.json \
29
+ --train dataset/data/train.jsonl --dev dataset/data/validation.jsonl \
30
+ --output rerun --steps 100 --batch-size 2 --accumulate 2 \
31
+ --max-length 256 --learning-rate 0.00002 --head-learning-rate 0.0001 \
32
+ --eval-every 25 --evaluation-dtype float32 --selection macro-f1 --seed 42
33
+ ```
34
+
35
+ The original run selected step 75 using development macro F1. No reserved test
36
+ examples are distributed or used by this command. Before running, verify the
37
+ training and validation file hashes against `training.json`.
reproducibility/environment.txt ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ accelerate==1.10.1
2
+ aiohappyeyeballs==2.6.1
3
+ aiohttp==3.12.7
4
+ aiosignal==1.3.2
5
+ attrs==25.3.0
6
+ cbor2==5.7.0
7
+ certifi==2026.7.22
8
+ charset-normalizer==3.5.2
9
+ cloudpickle==3.1.2
10
+ filelock==4.0.12
11
+ frozenlist==1.6.0
12
+ fsspec==2026.9.0
13
+ grpclib==0.4.8
14
+ h2==4.2.0
15
+ hf-xet==1.7.0
16
+ hpack==4.1.0
17
+ huggingface_hub==0.36.2
18
+ hyperframe==6.1.0
19
+ idna==3.20
20
+ Jinja2==3.1.6
21
+ joblib==1.6.0
22
+ MarkupSafe==3.0.4
23
+ mpmath==1.3.0
24
+ multidict==6.4.4
25
+ networkx==3.7
26
+ numpy==2.5.3
27
+ nvidia-cublas-cu12==12.8.4.1
28
+ nvidia-cuda-cupti-cu12==12.8.90
29
+ nvidia-cuda-nvrtc-cu12==12.8.93
30
+ nvidia-cuda-runtime-cu12==12.8.90
31
+ nvidia-cudnn-cu12==9.10.2.21
32
+ nvidia-cufft-cu12==11.3.3.83
33
+ nvidia-cufile-cu12==1.13.1.3
34
+ nvidia-curand-cu12==10.3.9.90
35
+ nvidia-cusolver-cu12==11.7.3.90
36
+ nvidia-cusparse-cu12==12.5.8.93
37
+ nvidia-cusparselt-cu12==0.7.1
38
+ nvidia-nccl-cu12==2.27.3
39
+ nvidia-nvjitlink-cu12==12.8.93
40
+ nvidia-nvtx-cu12==12.8.90
41
+ packaging==26.3
42
+ peft==0.18.1
43
+ propcache==0.3.1
44
+ protobuf==6.31.1
45
+ psutil==7.2.2
46
+ PyYAML==6.0.3
47
+ regex==2026.9.29
48
+ requests==2.34.2
49
+ safetensors==0.8.0
50
+ scikit-learn==1.7.2
51
+ scipy==1.18.1
52
+ setuptools==84.0.0
53
+ sympy==1.14.0
54
+ threadpoolctl==3.7.0
55
+ tokenizers==0.22.2
56
+ torch==2.8.0
57
+ tqdm==4.70.1
58
+ transformers==4.57.6
59
+ triton==3.4.0
60
+ typing_extensions==4.16.0
61
+ urllib3==2.8.0
62
+ uv==0.7.19
63
+ wheel==0.45.1
64
+ yarl==1.20.0
reproducibility/sequence_repair/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ """Reproducible continuation of fixed-label sequence classifiers."""
reproducibility/sequence_repair/complete_adapter.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Make a legacy sequence adapter self-contained with its original task head."""
2
+
3
+ # ruff: noqa: PLC0415
4
+ import argparse
5
+ import hashlib
6
+ import json
7
+ import shutil
8
+ from pathlib import Path
9
+
10
+ from .model import load_model
11
+ from .task_head import verify_saved_task_head
12
+
13
+
14
+ def main():
15
+ import torch
16
+ from safetensors.torch import load_file, save_file
17
+
18
+ parser = argparse.ArgumentParser()
19
+ parser.add_argument("--base", required=True)
20
+ parser.add_argument("--adapter", type=Path, required=True)
21
+ parser.add_argument("--contract", required=True)
22
+ parser.add_argument("--output", type=Path, required=True)
23
+ args = parser.parse_args()
24
+ if args.output.exists():
25
+ raise ValueError("Refusing to overwrite a completed adapter")
26
+ config = json.loads((args.adapter / "adapter_config.json").read_text())
27
+ if "head" in (config.get("modules_to_save") or []):
28
+ raise ValueError("Adapter already owns its task head")
29
+ torch.set_num_threads(8)
30
+ model, tokenizer, _labels, _ids = load_model(args.base, args.contract, args.adapter)
31
+ model.eval()
32
+ tokens = tokenizer(
33
+ ["A task head parity probe.", "核对分类头参数是否保持不变。"],
34
+ padding=True,
35
+ return_tensors="pt",
36
+ )
37
+ with torch.inference_mode():
38
+ before = model(**tokens).logits
39
+ head = model.get_base_model().head.state_dict()
40
+ weights = load_file(args.adapter / "adapter_model.safetensors", device="cpu")
41
+ for name, tensor in head.items():
42
+ key = "base_model.model.head." + name
43
+ if key in weights:
44
+ raise ValueError("Unexpected existing head tensor in legacy adapter")
45
+ weights[key] = tensor.detach().cpu().contiguous()
46
+ args.output.mkdir(parents=True)
47
+ for path in args.adapter.iterdir():
48
+ if path.is_file() and path.name not in {
49
+ "adapter_config.json",
50
+ "adapter_model.safetensors",
51
+ "task-head.json",
52
+ }:
53
+ shutil.copy2(path, args.output / path.name)
54
+ config["modules_to_save"] = [*(config.get("modules_to_save") or []), "head"]
55
+ (args.output / "adapter_config.json").write_text(
56
+ json.dumps(config, indent=2) + "\n"
57
+ )
58
+ save_file(
59
+ weights, args.output / "adapter_model.safetensors", metadata={"format": "pt"}
60
+ )
61
+ restored, _tokenizer, _labels, _ids = load_model(
62
+ args.base, args.contract, args.output
63
+ )
64
+ restored.eval()
65
+ with torch.inference_mode():
66
+ after = restored(**tokens).logits
67
+ if not bool(torch.isfinite(before).all() and torch.isfinite(after).all()):
68
+ raise ValueError("Task-head completion produced non-finite logits")
69
+ torch.testing.assert_close(before, after, rtol=1e-6, atol=1e-6)
70
+ scope = verify_saved_task_head(restored, args.output / "adapter_model.safetensors")
71
+ (args.output / "task-head.json").write_text(json.dumps(scope, indent=2) + "\n")
72
+ receipt = {
73
+ "operation": "Preserve the existing sequence task head in a legacy adapter; no training or encoder change",
74
+ "source_adapter_sha256": hashlib.sha256(
75
+ (args.adapter / "adapter_model.safetensors").read_bytes()
76
+ ).hexdigest(),
77
+ "completed_adapter_sha256": hashlib.sha256(
78
+ (args.output / "adapter_model.safetensors").read_bytes()
79
+ ).hexdigest(),
80
+ "base_model": config["base_model_name_or_path"],
81
+ "base_revision": config.get("revision"),
82
+ "head_tensors": list(head),
83
+ "task_head": scope,
84
+ "maximum_absolute_logit_difference": float((before - after).abs().max()),
85
+ "quality_evaluation": False,
86
+ }
87
+ (args.output / "task-head-completion.json").write_text(
88
+ json.dumps(receipt, indent=2) + "\n"
89
+ )
90
+ print(json.dumps(receipt))
91
+
92
+
93
+ if __name__ == "__main__":
94
+ main()
reproducibility/sequence_repair/context.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Construct measured context probes without truncating the task-bearing payload."""
2
+
3
+
4
+ def insert_payload(
5
+ payload,
6
+ background,
7
+ position,
8
+ budget,
9
+ count_tokens,
10
+ *,
11
+ fill_to_budget=True,
12
+ neutral_suffix=" note"
13
+ ):
14
+ if position not in {"head", "middle", "tail"}:
15
+ raise ValueError("Unknown payload position")
16
+ if not payload or not background.strip() or budget <= 0:
17
+ raise ValueError("Payload, background and token budget must be nonempty")
18
+ if count_tokens(payload) > budget:
19
+ raise ValueError("Payload itself exceeds the token budget")
20
+ if count_tokens(background) <= count_tokens(""):
21
+ raise ValueError("Background must contribute actual tokens")
22
+
23
+ def build(repetitions):
24
+ left = {"head": 0, "middle": repetitions // 2, "tail": repetitions}[position]
25
+ prefix = "\n".join([background] * left)
26
+ suffix = "\n".join([background] * (repetitions - left))
27
+ text = (
28
+ (prefix + "\n" if prefix else "")
29
+ + payload
30
+ + ("\n" + suffix if suffix else "")
31
+ )
32
+ start = len(prefix) + 1 if prefix else 0
33
+ return text, start
34
+
35
+ measured = {}
36
+
37
+ def measure(repetitions):
38
+ if repetitions not in measured:
39
+ measured[repetitions] = count_tokens(build(repetitions)[0])
40
+ return measured[repetitions]
41
+
42
+ # Repeated complete paragraphs usually have a constant token increment.
43
+ # Use that only to find a close starting point; exact measurements still
44
+ # bracket and determine the result, including boundary-token differences.
45
+ increment = max(1, measure(2) - measure(1))
46
+ estimate = min(budget, max(0, 1 + (budget - measure(1)) // increment))
47
+ if measure(estimate) <= budget:
48
+ low, high, distance = estimate, estimate, 1
49
+ while high < budget:
50
+ high = min(budget, estimate + distance)
51
+ if measure(high) > budget:
52
+ break
53
+ low, distance = high, distance * 2
54
+ else:
55
+ low, high, distance = estimate, estimate, 1
56
+ while low > 0:
57
+ low = max(0, estimate - distance)
58
+ if measure(low) <= budget:
59
+ break
60
+ high, distance = low, distance * 2
61
+ while low < high:
62
+ middle = (low + high + 1) // 2
63
+ if measure(middle) <= budget:
64
+ low = middle
65
+ else:
66
+ high = middle - 1
67
+ text, start = build(low)
68
+ actual = measure(low)
69
+ if fill_to_budget:
70
+ text += neutral_suffix * (budget - actual)
71
+ actual = count_tokens(text)
72
+ if actual != budget:
73
+ raise ValueError(
74
+ "Neutral suffix does not fill this tokenizer's exact budget"
75
+ )
76
+ if actual > budget or text[start : start + len(payload)] != payload:
77
+ raise ValueError(
78
+ "Context construction corrupted its payload or exceeded budget"
79
+ )
80
+ return {
81
+ "text": text,
82
+ "payload_start": start,
83
+ "payload_end": start + len(payload),
84
+ "actual_tokens": actual,
85
+ }
reproducibility/sequence_repair/corpus.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Frozen task corpus receipts and source-group partitioning."""
2
+
3
+ import hashlib
4
+ import json
5
+ from pathlib import Path
6
+
7
+ from .data import assert_disjoint, normalized_text
8
+
9
+
10
+ def group_split(group, seed=20260913):
11
+ bucket = int(hashlib.sha256(f"{seed}:{group}".encode()).hexdigest()[:8], 16) % 100
12
+ return "train" if bucket < 70 else "dev" if bucket < 85 else "test" # noqa: PLR2004
13
+
14
+
15
+ def write_corpus(output, splits, contract, provenance):
16
+ output = Path(output)
17
+ if output.exists() and any(output.iterdir()):
18
+ raise ValueError("Refusing to overwrite a frozen corpus")
19
+ unique = {}
20
+ for split, rows in splits.items():
21
+ seen, retained, retained_keys = {}, [], set()
22
+ for row in rows:
23
+ key = normalized_text(row["text"])
24
+ previous = seen.setdefault(key, row["label"])
25
+ if previous != row["label"]:
26
+ raise ValueError("Contradictory labels for identical normalized text")
27
+ if key in unique and unique[key] != split:
28
+ raise ValueError("Duplicate text crosses corpus splits")
29
+ unique[key] = split
30
+ if key not in retained_keys:
31
+ retained.append(row)
32
+ retained_keys.add(key)
33
+ splits[split] = retained
34
+ for first, second in [("train", "dev"), ("train", "test"), ("dev", "test")]:
35
+ assert_disjoint(splits[first], splits[second])
36
+ output.mkdir(parents=True, exist_ok=True)
37
+ files = {}
38
+ for split, rows in splits.items():
39
+ content = "".join(
40
+ json.dumps(row, ensure_ascii=False, sort_keys=True) + "\n" for row in rows
41
+ )
42
+ (output / f"{split}.jsonl").write_text(content)
43
+ files[split] = {
44
+ "rows": len(rows),
45
+ "sha256": hashlib.sha256(content.encode()).hexdigest(),
46
+ }
47
+ (output / "contract.json").write_text(json.dumps(contract, indent=2) + "\n")
48
+ manifest = {
49
+ "files": files,
50
+ "provenance": provenance,
51
+ "test_used_for_selection": False,
52
+ "split_policy": "Group-disjoint before sampling; exact NFKC/case/whitespace overlap rejected",
53
+ }
54
+ (output / "manifest.json").write_text(json.dumps(manifest, indent=2) + "\n")
55
+ return manifest
reproducibility/sequence_repair/data.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Dependency-light sequence data contracts and classification metrics."""
2
+
3
+ import hashlib
4
+ import json
5
+ import unicodedata
6
+ from collections import Counter
7
+ from pathlib import Path
8
+
9
+
10
+ def normalized_text(text):
11
+ return " ".join(unicodedata.normalize("NFKC", text).casefold().split())
12
+
13
+
14
+ def load_contract(path):
15
+ config = json.loads(Path(path).read_text())
16
+ id_to_label = {int(index): label for index, label in config["id2label"].items()}
17
+ if set(id_to_label) != set(range(len(id_to_label))):
18
+ raise ValueError("Label IDs must be contiguous from zero")
19
+ if config["label2id"] != {label: index for index, label in id_to_label.items()}:
20
+ raise ValueError("id2label and label2id must be a bijection")
21
+ return config["label2id"], id_to_label
22
+
23
+
24
+ def read_records(paths, label_to_id):
25
+ records, ids, texts = [], set(), {}
26
+ for path in paths:
27
+ for line in Path(path).read_text().split("\n"):
28
+ if not line.strip():
29
+ continue
30
+ row = json.loads(line)
31
+ if not isinstance(row.get("text"), str) or not row["text"].strip():
32
+ raise ValueError("A classification row needs nonempty text")
33
+ if not row.get("id") or row["id"] in ids:
34
+ raise ValueError("Missing or duplicate row ID")
35
+ if not row.get("group_id"):
36
+ raise ValueError("Each row needs a source/translation/template group")
37
+ if row.get("label") not in label_to_id:
38
+ raise ValueError("Unknown classification label")
39
+ normalized = normalized_text(row["text"])
40
+ previous = texts.setdefault(normalized, row["label"])
41
+ if previous != row["label"]:
42
+ raise ValueError("Identical text has contradictory labels")
43
+ ids.add(row["id"])
44
+ records.append(row)
45
+ if not records:
46
+ raise ValueError(
47
+ "Empty datasets cannot produce training or evaluation evidence"
48
+ )
49
+ return records
50
+
51
+
52
+ def assert_disjoint(train, dev):
53
+ for key, transform in [("group_id", str), ("text", normalized_text), ("id", str)]:
54
+ overlap = {transform(row[key]) for row in train} & {
55
+ transform(row[key]) for row in dev
56
+ }
57
+ if overlap:
58
+ raise ValueError(f"Training/development overlap in {key}: {len(overlap)}")
59
+
60
+
61
+ def file_receipts(paths):
62
+ return [
63
+ {
64
+ "file": Path(path).name,
65
+ "sha256": hashlib.sha256(Path(path).read_bytes()).hexdigest(),
66
+ }
67
+ for path in paths
68
+ ]
69
+
70
+
71
+ def classification_metrics(gold, predicted, id_to_label):
72
+ if not gold or len(gold) != len(predicted):
73
+ raise ValueError("Empty or mismatched prediction evidence")
74
+ count = len(id_to_label)
75
+ confusion = [[0] * count for _ in range(count)]
76
+ for index, expected in enumerate(gold):
77
+ actual = predicted[index]
78
+ if expected not in id_to_label or actual not in id_to_label:
79
+ raise ValueError("Prediction outside the frozen label contract")
80
+ confusion[expected][actual] += 1
81
+ per_label = {}
82
+ for index, label in id_to_label.items():
83
+ tp = confusion[index][index]
84
+ support = sum(confusion[index])
85
+ selected = sum(row[index] for row in confusion)
86
+ per_label[label] = {
87
+ "support": support,
88
+ "tp": tp,
89
+ "fp": selected - tp,
90
+ "fn": support - tp,
91
+ "precision": tp / selected if selected else 0.0,
92
+ "recall": tp / support if support else 0.0,
93
+ "f1": 2 * tp / (selected + support) if selected + support else 0.0,
94
+ }
95
+ present = [row for row in per_label.values() if row["support"]]
96
+ return {
97
+ "rows": len(gold),
98
+ "accuracy": sum(confusion[index][index] for index in range(count)) / len(gold),
99
+ "macro_f1": sum(row["f1"] for row in per_label.values()) / count,
100
+ "macro_f1_present_labels": sum(row["f1"] for row in present) / len(present),
101
+ "per_label": per_label,
102
+ "confusion": confusion,
103
+ }
104
+
105
+
106
+ def label_counts(records):
107
+ return dict(Counter(row["label"] for row in records))
reproducibility/sequence_repair/evaluate.py ADDED
@@ -0,0 +1,147 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Explicit streaming evaluation; never invoked on test data during training."""
2
+
3
+ # ruff: noqa: PLC0415
4
+
5
+ import argparse
6
+ import json
7
+ import time
8
+ from pathlib import Path
9
+
10
+ from .data import classification_metrics, file_receipts, read_records
11
+ from .model import load_model
12
+
13
+ LONG_BATCH_THRESHOLD = 2048
14
+
15
+
16
+ def evaluate_records(
17
+ model,
18
+ tokenizer,
19
+ records,
20
+ label_to_id,
21
+ id_to_label,
22
+ max_length,
23
+ batch_size=16,
24
+ dtype="bfloat16",
25
+ ):
26
+ import torch
27
+
28
+ device = next(model.parameters()).device
29
+ if model.config.problem_type != "single_label_classification":
30
+ raise ValueError("Softmax evaluation requires a single-label classifier")
31
+ was_training = model.training
32
+ model.eval()
33
+ encoded = [
34
+ tokenizer(row["text"], truncation=False, padding=False) for row in records
35
+ ]
36
+ lengths = [len(row["input_ids"]) for row in encoded]
37
+ if max(lengths) > max_length:
38
+ raise ValueError(
39
+ "Evaluation input exceeds the explicit budget; truncation is forbidden"
40
+ )
41
+ order = sorted(range(len(records)), key=lengths.__getitem__)
42
+ results, timings, cursor = [None] * len(records), [], 0
43
+ while cursor < len(order):
44
+ size = 1 if lengths[order[cursor]] > LONG_BATCH_THRESHOLD else batch_size
45
+ indices = order[cursor : cursor + size]
46
+ if any(lengths[index] > LONG_BATCH_THRESHOLD for index in indices):
47
+ indices = indices[:1]
48
+ cursor += len(indices)
49
+ batch = tokenizer.pad(
50
+ [encoded[index] for index in indices], padding=True, return_tensors="pt"
51
+ )
52
+ batch = {key: value.to(device) for key, value in batch.items()}
53
+ if device.type == "cuda":
54
+ torch.cuda.synchronize()
55
+ start = time.perf_counter()
56
+ with torch.inference_mode(), torch.autocast(
57
+ device_type=device.type,
58
+ dtype=getattr(torch, dtype),
59
+ enabled=device.type == "cuda" and dtype != "float32",
60
+ ):
61
+ logits = model(**batch).logits.float()
62
+ if not bool(torch.isfinite(logits).all()):
63
+ raise ValueError("Non-finite evaluation logits")
64
+ probabilities = logits.softmax(-1).cpu().tolist()
65
+ if device.type == "cuda":
66
+ torch.cuda.synchronize()
67
+ timings.append((time.perf_counter() - start) * 1000)
68
+ for position, index in enumerate(indices):
69
+ scores = probabilities[position]
70
+ results[index] = {
71
+ "id": records[index]["id"],
72
+ "prediction": max(range(len(scores)), key=scores.__getitem__),
73
+ "probabilities": scores,
74
+ "tokens": lengths[index],
75
+ }
76
+ gold = [label_to_id[row["label"]] for row in records]
77
+ predicted = [row["prediction"] for row in results]
78
+ metrics = classification_metrics(gold, predicted, id_to_label)
79
+ metrics["breakdowns"] = {}
80
+ for field in ["source", "language", "length_bucket", "position"]:
81
+ metrics["breakdowns"][field] = {}
82
+ for group in sorted({str(row.get(field, "unspecified")) for row in records}):
83
+ indices = [
84
+ i
85
+ for i, row in enumerate(records)
86
+ if str(row.get(field, "unspecified")) == group
87
+ ]
88
+ metrics["breakdowns"][field][group] = classification_metrics(
89
+ [gold[i] for i in indices], [predicted[i] for i in indices], id_to_label
90
+ )
91
+ metrics["actual_tokens"] = {"min": min(lengths), "max": max(lengths)}
92
+ metrics["forward_batches_ms"] = timings
93
+ metrics["precision"] = {
94
+ "parameters": "float32",
95
+ "forward_autocast": (
96
+ dtype if device.type == "cuda" and dtype != "float32" else None
97
+ ),
98
+ "device_type": device.type,
99
+ }
100
+ if was_training:
101
+ model.train()
102
+ return metrics, results
103
+
104
+
105
+ def main():
106
+ import torch
107
+
108
+ torch.set_num_threads(8)
109
+ parser = argparse.ArgumentParser()
110
+ parser.add_argument("--base", required=True)
111
+ parser.add_argument("--adapter")
112
+ parser.add_argument("--contract", required=True)
113
+ parser.add_argument("--data", nargs="+", required=True)
114
+ parser.add_argument("--output", type=Path, required=True)
115
+ parser.add_argument("--max-length", type=int, default=32768)
116
+ parser.add_argument("--device", default="cuda")
117
+ parser.add_argument("--dtype", choices=["float32", "bfloat16"], default="bfloat16")
118
+ args = parser.parse_args()
119
+ if args.output.exists():
120
+ raise ValueError("Refusing to overwrite evaluation evidence")
121
+ model, tokenizer, labels, ids = load_model(args.base, args.contract, args.adapter)
122
+ if args.max_length > model.config.max_position_embeddings:
123
+ raise ValueError("Evaluation budget exceeds checkpoint position capacity")
124
+ model.to(args.device)
125
+ rows = read_records(args.data, labels)
126
+ metrics, predictions = evaluate_records(
127
+ model, tokenizer, rows, labels, ids, args.max_length, dtype=args.dtype
128
+ )
129
+ metrics["data_files"] = file_receipts(args.data)
130
+ args.output.parent.mkdir(parents=True, exist_ok=True)
131
+ args.output.write_text(json.dumps(metrics, indent=2) + "\n")
132
+ args.output.with_suffix(".predictions.jsonl").write_text(
133
+ "".join(json.dumps(row) + "\n" for row in predictions)
134
+ )
135
+ print(
136
+ json.dumps(
137
+ {
138
+ key: metrics[key]
139
+ for key in ["rows", "accuracy", "macro_f1", "actual_tokens"]
140
+ }
141
+ ),
142
+ flush=True,
143
+ )
144
+
145
+
146
+ if __name__ == "__main__":
147
+ main()
reproducibility/sequence_repair/export.py ADDED
@@ -0,0 +1,172 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Freeze a selected adapter or full model as a standard HF sequence checkpoint."""
2
+
3
+ # ruff: noqa: PLC0415
4
+
5
+ import argparse
6
+ import hashlib
7
+ import json
8
+ from pathlib import Path
9
+
10
+ from .data import load_contract
11
+ from .model import configuration_overrides, load_model, load_task_config
12
+ from .optimization import validate_method, verify_full_checkpoint
13
+ from .runtime_mapping import RUNTIME_TASKS, write_runtime_mappings
14
+ from .task_head import (
15
+ assert_task_head_preserved,
16
+ task_head_scope,
17
+ verify_saved_task_head,
18
+ )
19
+
20
+
21
+ def validate_provenance(args):
22
+ validate_method(args.method, args.adapter, False)
23
+ run = json.loads(args.run_manifest.read_text())
24
+ if run.get("test_used") is not False:
25
+ raise ValueError("Candidate provenance must explicitly exclude test selection")
26
+ if run.get("base_revision") != args.base_revision:
27
+ raise ValueError("Export base revision differs from the training receipt")
28
+ if run.get("base_model", args.base_id) != args.base_id:
29
+ raise ValueError("Export base model differs from the training receipt")
30
+ if run.get("method", "lora") != args.method:
31
+ raise ValueError("Export method differs from the training receipt")
32
+ if args.method == "full":
33
+ origin = json.loads((Path(args.base) / "training-origin.json").read_text())
34
+ expected = {
35
+ "method": "full",
36
+ "base_model": args.base_id,
37
+ "base_revision": args.base_revision,
38
+ }
39
+ if origin != expected:
40
+ raise ValueError("Full checkpoint origin differs from the training receipt")
41
+ config = json.loads((Path(args.base) / "config.json").read_text())
42
+ labels, ids = load_contract(args.contract)
43
+ contract = configuration_overrides(args.contract)
44
+ contract.update(label2id=labels, id2label={str(k): v for k, v in ids.items()})
45
+ if any(config.get(key) != value for key, value in contract.items()):
46
+ raise ValueError("Export contract differs from the trained full checkpoint")
47
+
48
+
49
+ def main():
50
+ import torch
51
+
52
+ parser = argparse.ArgumentParser()
53
+ parser.add_argument("--base", required=True)
54
+ parser.add_argument("--base-id", required=True)
55
+ parser.add_argument("--base-revision", required=True)
56
+ parser.add_argument("--method", choices=["lora", "full"], default="lora")
57
+ parser.add_argument("--adapter", type=Path)
58
+ parser.add_argument("--contract", required=True)
59
+ parser.add_argument("--output", type=Path, required=True)
60
+ parser.add_argument("--run-manifest", type=Path, required=True)
61
+ parser.add_argument("--runtime-task", choices=RUNTIME_TASKS, required=True)
62
+ args = parser.parse_args()
63
+ if args.output.exists() and any(args.output.iterdir()):
64
+ raise ValueError("Refusing to overwrite a frozen candidate")
65
+ validate_provenance(args)
66
+ torch.set_num_threads(8)
67
+ model, tokenizer, labels, _ = load_model(args.base, args.contract, args.adapter)
68
+ model.eval()
69
+ if args.method == "full":
70
+ verify_full_checkpoint(model, args.base)
71
+ head = task_head_scope(model)
72
+ else:
73
+ head = verify_saved_task_head(model, args.adapter / "adapter_model.safetensors")
74
+ tokens = tokenizer(
75
+ ["Numerical checkpoint equivalence probe.", "用于核对模型数值的输入。"],
76
+ padding=True,
77
+ return_tensors="pt",
78
+ )
79
+ with torch.inference_mode():
80
+ before = model(**tokens).logits
81
+ exported = (
82
+ model.merge_and_unload(safe_merge=True) if args.method == "lora" else model
83
+ )
84
+ after = exported(**tokens).logits
85
+ assert_task_head_preserved(head, exported)
86
+ torch.testing.assert_close(before, after, atol=2e-4, rtol=2e-4)
87
+ if type(exported).__name__ != "ModernBertForSequenceClassification":
88
+ raise ValueError(
89
+ "Export must preserve the standard supported HF sequence architecture"
90
+ )
91
+ if exported.config.label2id != labels:
92
+ raise ValueError("Export changed label IDs")
93
+ exported.config._name_or_path = args.base_id
94
+ args.output.mkdir(parents=True, exist_ok=True)
95
+ exported.save_pretrained(args.output, safe_serialization=True)
96
+ verify_full_checkpoint(exported, args.output)
97
+ tokenizer.save_pretrained(args.output)
98
+ restored = (
99
+ type(exported)
100
+ .from_pretrained(
101
+ args.output,
102
+ config=load_task_config(args.output),
103
+ torch_dtype=torch.float32,
104
+ attn_implementation="sdpa",
105
+ )
106
+ .eval()
107
+ )
108
+ verify_full_checkpoint(restored, args.output)
109
+ assert_task_head_preserved(head, restored)
110
+ with torch.inference_mode():
111
+ reloaded_logits = restored(**tokens).logits
112
+ torch.testing.assert_close(after, reloaded_logits, atol=0, rtol=0)
113
+ mapping_files = write_runtime_mappings(args.output, labels, args.runtime_task)
114
+ receipt = {
115
+ "method": args.method,
116
+ "base_model": args.base_id,
117
+ "base_revision": args.base_revision,
118
+ "run_manifest_sha256": hashlib.sha256(
119
+ args.run_manifest.read_bytes()
120
+ ).hexdigest(),
121
+ "parameter_count": sum(
122
+ parameter.numel() for parameter in exported.parameters()
123
+ ),
124
+ "encoder_parameter_count": sum(
125
+ parameter.numel() for parameter in exported.model.parameters()
126
+ ),
127
+ "label2id": labels,
128
+ "runtime_task": args.runtime_task,
129
+ "runtime_mapping_files": mapping_files,
130
+ "task": exported.config.problem_type,
131
+ "classifier_pooling": exported.config.classifier_pooling,
132
+ "max_position_embeddings": exported.config.max_position_embeddings,
133
+ "position_capacity_is_quality_evidence": False,
134
+ "weight_dtype": "float32",
135
+ "task_head": head,
136
+ "task_head_preserved_after_reload": True,
137
+ "all_tensors_preserved_after_reload": True,
138
+ "test_used_for_selection": False,
139
+ "files": {
140
+ path.name: hashlib.sha256(path.read_bytes()).hexdigest()
141
+ for path in sorted(args.output.iterdir())
142
+ if path.is_file()
143
+ },
144
+ }
145
+ if args.method == "lora":
146
+ receipt.update(
147
+ adapter_sha256=hashlib.sha256(
148
+ (args.adapter / "adapter_model.safetensors").read_bytes()
149
+ ).hexdigest(),
150
+ task_head_preserved_after_merge_and_reload=True,
151
+ merge_probe_max_absolute_difference=float((before - after).abs().max()),
152
+ )
153
+ (args.output / "candidate-lock.json").write_text(
154
+ json.dumps(receipt, indent=2) + "\n"
155
+ )
156
+ print(
157
+ json.dumps(
158
+ {
159
+ key: receipt[key]
160
+ for key in (
161
+ "method",
162
+ "parameter_count",
163
+ "all_tensors_preserved_after_reload",
164
+ )
165
+ }
166
+ ),
167
+ flush=True,
168
+ )
169
+
170
+
171
+ if __name__ == "__main__":
172
+ main()
reproducibility/sequence_repair/fetch_sources.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Fetch only the pinned public dataset files used by the repair recipes."""
2
+
3
+ import argparse
4
+ import hashlib
5
+ import json
6
+ from pathlib import Path
7
+
8
+
9
+ def main():
10
+ from huggingface_hub import hf_hub_download # noqa: PLC0415
11
+
12
+ parser = argparse.ArgumentParser()
13
+ parser.add_argument(
14
+ "--manifest",
15
+ type=Path,
16
+ default=Path(__file__).parent / "data/source-files.json",
17
+ )
18
+ parser.add_argument("--output", type=Path, required=True)
19
+ args = parser.parse_args()
20
+ receipts = []
21
+ for item in json.loads(args.manifest.read_text()):
22
+ directory = args.output / item["repo"].replace("/", "--")
23
+ path = Path(
24
+ hf_hub_download(
25
+ repo_id=item["repo"],
26
+ repo_type="dataset",
27
+ filename=item["file"],
28
+ revision=item["revision"],
29
+ local_dir=directory,
30
+ )
31
+ )
32
+ digest = hashlib.sha256(path.read_bytes()).hexdigest()
33
+ if digest != item["sha256"]:
34
+ raise ValueError(
35
+ f"Pinned dataset file hash differs: {item['repo']}/{item['file']}"
36
+ )
37
+ receipts.append({**item, "verified_sha256": digest})
38
+ print(
39
+ json.dumps({"repo": item["repo"], "file": item["file"], "verified": True}),
40
+ flush=True,
41
+ )
42
+ (args.output / "verified-source-files.json").write_text(
43
+ json.dumps(receipts, indent=2) + "\n"
44
+ )
45
+
46
+
47
+ if __name__ == "__main__":
48
+ main()
reproducibility/sequence_repair/hydrate_annotations.py ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Reconstruct reviewed requests from pinned source data and text-free label sidecars."""
2
+
3
+ # ruff: noqa: PLC0415
4
+ import argparse
5
+ import hashlib
6
+ import json
7
+ from pathlib import Path
8
+
9
+
10
+ def sha256_text(text):
11
+ return hashlib.sha256(text.encode()).hexdigest()
12
+
13
+
14
+ def hydrate_records(sidecar, sources):
15
+ repository = sidecar["source"]["repository"]
16
+ result = []
17
+ if repository == "CohereLabs/aya_dataset":
18
+ lookup = {}
19
+ for row in sources:
20
+ if not row.get("user_id"):
21
+ continue
22
+ key = (
23
+ sha256_text(row["inputs"]),
24
+ row["language_code"],
25
+ "aya-author-" + sha256_text(row["user_id"]),
26
+ )
27
+ lookup[key] = row["inputs"]
28
+ for row in sidecar["records"]:
29
+ key = (row["source_text_sha256"], row["language"], row["group_id"])
30
+ if key not in lookup:
31
+ raise ValueError(
32
+ "Reviewed Aya request or author differs from the pinned source"
33
+ )
34
+ result.append({**row, "text": lookup[key]})
35
+ elif repository == "databricks/databricks-dolly-15k":
36
+ for row in sidecar["records"]:
37
+ source = sources[row["source_row"]]
38
+ instruction, context = (
39
+ source["instruction"].strip(),
40
+ source["context"].strip(),
41
+ )
42
+ text = instruction + ("\n\n" + context if context else "")
43
+ if sha256_text(text) != row["source_text_sha256"]:
44
+ raise ValueError(
45
+ "Reviewed Dolly request differs from the pinned source"
46
+ )
47
+ result.append(
48
+ {**row, "text": text, "instruction": instruction, "context": context}
49
+ )
50
+ else:
51
+ raise ValueError("Unknown reviewed source")
52
+ return result
53
+
54
+
55
+ def main():
56
+ parser = argparse.ArgumentParser()
57
+ parser.add_argument("--sidecar", type=Path, required=True)
58
+ parser.add_argument("--source", type=Path, required=True)
59
+ parser.add_argument("--output", type=Path, required=True)
60
+ args = parser.parse_args()
61
+ if args.output.exists():
62
+ raise ValueError("Refusing to overwrite reviewed annotations")
63
+ sidecar = json.loads(args.sidecar.read_text())
64
+ if (
65
+ hashlib.sha256(args.source.read_bytes()).hexdigest()
66
+ != sidecar["source"]["file_sha256"]
67
+ ):
68
+ raise ValueError("Source file hash differs from the reviewed revision")
69
+ if sidecar["source"]["repository"] == "CohereLabs/aya_dataset":
70
+ from pyarrow import parquet
71
+
72
+ sources = parquet.read_table(
73
+ args.source, columns=["inputs", "language_code", "user_id"]
74
+ ).to_pylist()
75
+ else:
76
+ sources = [
77
+ json.loads(line)
78
+ for line in args.source.read_text().split("\n")
79
+ if line.strip()
80
+ ]
81
+ rows = hydrate_records(sidecar, sources)
82
+ content = "".join(
83
+ json.dumps(row, ensure_ascii=False, sort_keys=True) + "\n" for row in rows
84
+ )
85
+ args.output.parent.mkdir(parents=True, exist_ok=True)
86
+ args.output.write_text(content)
87
+ print(
88
+ json.dumps(
89
+ {"rows": len(rows), "sha256": hashlib.sha256(content.encode()).hexdigest()}
90
+ )
91
+ )
92
+
93
+
94
+ if __name__ == "__main__":
95
+ main()
reproducibility/sequence_repair/initialize.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Initialize a fresh task adapter and trainable head from a fixed encoder base."""
2
+
3
+ # ruff: noqa: PLC0415
4
+ import argparse
5
+ import hashlib
6
+ import json
7
+ from pathlib import Path
8
+
9
+ from .model import save_adapter
10
+ from .optimization import load_trainable_model
11
+
12
+
13
+ def main():
14
+ import torch
15
+ from peft import LoraConfig, TaskType, get_peft_model
16
+
17
+ parser = argparse.ArgumentParser()
18
+ parser.add_argument("--base", required=True)
19
+ parser.add_argument("--base-id", required=True)
20
+ parser.add_argument("--base-revision", required=True)
21
+ parser.add_argument("--contract", type=Path, required=True)
22
+ parser.add_argument("--output", type=Path, required=True)
23
+ parser.add_argument("--seed", type=int, default=20260913)
24
+ parser.add_argument("--rank", type=int, default=32)
25
+ parser.add_argument("--alpha", type=int, default=64)
26
+ parser.add_argument("--dropout", type=float, default=0.05)
27
+ args = parser.parse_args()
28
+ if args.output.exists():
29
+ raise ValueError("Refusing to overwrite an initialized adapter")
30
+ if min(args.rank, args.alpha) <= 0 or not 0 <= args.dropout < 1:
31
+ raise ValueError("Invalid LoRA dimensions or dropout")
32
+ torch.set_num_threads(8)
33
+ torch.manual_seed(args.seed)
34
+ model, tokenizer, _labels, _ids = load_trainable_model(
35
+ args.base, args.contract, None, "full", fresh_head=True
36
+ )
37
+ model = get_peft_model(
38
+ model,
39
+ LoraConfig(
40
+ task_type=TaskType.SEQ_CLS,
41
+ r=args.rank,
42
+ lora_alpha=args.alpha,
43
+ lora_dropout=args.dropout,
44
+ target_modules=["attn.Wqkv", "attn.Wo", "mlp.Wi", "mlp.Wo"],
45
+ modules_to_save=["head", "classifier"],
46
+ bias="none",
47
+ revision=args.base_revision,
48
+ ),
49
+ )
50
+ save_adapter(model, tokenizer, args.output, args.base_id, args.base_revision)
51
+ (args.output / "contract.json").write_bytes(args.contract.read_bytes())
52
+ receipt = {
53
+ "base_model": args.base_id,
54
+ "base_revision": args.base_revision,
55
+ "seed": args.seed,
56
+ "rank": args.rank,
57
+ "alpha": args.alpha,
58
+ "dropout": args.dropout,
59
+ "classifier_pooling": model.config.classifier_pooling,
60
+ "adapter_sha256": hashlib.sha256(
61
+ (args.output / "adapter_model.safetensors").read_bytes()
62
+ ).hexdigest(),
63
+ "trainable_parameters": sum(
64
+ parameter.numel()
65
+ for parameter in model.parameters()
66
+ if parameter.requires_grad
67
+ ),
68
+ }
69
+ (args.output / "initialization.json").write_text(
70
+ json.dumps(receipt, indent=2) + "\n"
71
+ )
72
+ print(json.dumps(receipt))
73
+
74
+
75
+ if __name__ == "__main__":
76
+ main()
reproducibility/sequence_repair/model.py ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """HF standard sequence checkpoints with an unchanged label contract."""
2
+
3
+ # Heavy imports stay optional for data-contract tests.
4
+ # ruff: noqa: PLC0415
5
+
6
+ import json
7
+ from pathlib import Path
8
+
9
+ from .data import load_contract
10
+ from .task_head import verify_saved_task_head
11
+
12
+
13
+ def configuration_overrides(contract):
14
+ """Validate optional standard HF task settings without guessing a pooling mode."""
15
+ content = json.loads(Path(contract).read_text())
16
+ problem_type = content.get("problem_type", "single_label_classification")
17
+ if problem_type not in {
18
+ "single_label_classification",
19
+ "multi_label_classification",
20
+ }:
21
+ raise ValueError("Unsupported classification problem type")
22
+ overrides = {"problem_type": problem_type}
23
+ if "classifier_pooling" in content:
24
+ pooling = content["classifier_pooling"]
25
+ if pooling not in {"mean", "cls"}:
26
+ raise ValueError("Sequence pooling must be explicitly mean or cls")
27
+ overrides["classifier_pooling"] = pooling
28
+ return overrides
29
+
30
+
31
+ def load_task_config(base, **overrides):
32
+ """Keep optional execution settings on the configuration that owns them."""
33
+ from transformers import AutoConfig
34
+
35
+ config = AutoConfig.from_pretrained(base, **overrides)
36
+ if hasattr(config, "reference_compile"):
37
+ config.reference_compile = False
38
+ return config
39
+
40
+
41
+ def load_model(base, contract, adapter=None, trainable=False):
42
+ import torch
43
+ from transformers import AutoModelForSequenceClassification, AutoTokenizer
44
+
45
+ label_to_id, id_to_label = load_contract(contract)
46
+ overrides = configuration_overrides(contract)
47
+ tokenizer = AutoTokenizer.from_pretrained(base)
48
+ config = load_task_config(
49
+ base,
50
+ num_labels=len(label_to_id),
51
+ label2id=label_to_id,
52
+ id2label=id_to_label,
53
+ **overrides,
54
+ )
55
+ model, loading = AutoModelForSequenceClassification.from_pretrained(
56
+ base,
57
+ config=config,
58
+ torch_dtype=torch.float32,
59
+ attn_implementation="sdpa",
60
+ output_loading_info=True,
61
+ )
62
+ missing_encoder = [
63
+ name for name in loading["missing_keys"] if name.startswith("model.")
64
+ ]
65
+ if missing_encoder:
66
+ raise ValueError(f"Checkpoint is missing encoder tensors: {missing_encoder}")
67
+ if adapter:
68
+ from peft import PeftModel
69
+
70
+ model = PeftModel.from_pretrained(model, adapter, is_trainable=trainable)
71
+ elif trainable:
72
+ raise ValueError("Continuation requires an explicit initial adapter")
73
+ if model.config.label2id != label_to_id:
74
+ raise ValueError("The loaded classifier changed the label contract")
75
+ return model, tokenizer, label_to_id, id_to_label
76
+
77
+
78
+ def save_adapter(model, tokenizer, destination, base_id, base_revision):
79
+ import json
80
+
81
+ destination.mkdir(parents=True, exist_ok=True)
82
+ model.peft_config["default"].base_model_name_or_path = base_id
83
+ model.peft_config["default"].revision = base_revision
84
+ model.save_pretrained(destination)
85
+ scope = verify_saved_task_head(model, destination / "adapter_model.safetensors")
86
+ (destination / "task-head.json").write_text(json.dumps(scope, indent=2) + "\n")
87
+ tokenizer.save_pretrained(destination)
88
+ (destination / "label_mapping.json").write_text(
89
+ json.dumps(
90
+ {"label2id": model.config.label2id, "id2label": model.config.id2label},
91
+ indent=2,
92
+ )
93
+ + "\n"
94
+ )
reproducibility/sequence_repair/optimization.py ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Explicit adapter or full-encoder optimization and checkpoint ownership."""
2
+
3
+ # ruff: noqa: PLC0415
4
+
5
+ import hashlib
6
+ import json
7
+ import math
8
+ from pathlib import Path
9
+
10
+ from .model import load_model, save_adapter
11
+ from .task_head import task_head_scope, tensor_digest
12
+
13
+
14
+ def configure_trainable_tail(model, last_layers):
15
+ """Restrict a complete ModernBERT checkpoint to its tail and entire task head."""
16
+ if (
17
+ type(model).__name__ != "ModernBertForSequenceClassification"
18
+ or hasattr(model, "peft_config")
19
+ or hasattr(model, "get_base_model")
20
+ ):
21
+ raise ValueError("Tail training requires a complete ModernBERT classifier")
22
+ layers = model.model.layers
23
+ if type(last_layers) is not int or not 1 <= last_layers <= len(layers):
24
+ raise ValueError("Trainable tail must contain one through all encoder layers")
25
+ start = len(layers) - last_layers
26
+ modules = [*layers[start:], model.head, model.classifier]
27
+ selected = {
28
+ id(parameter) for module in modules for parameter in module.parameters()
29
+ }
30
+ named = dict(model.named_parameters())
31
+ if not selected or not selected <= {id(parameter) for parameter in named.values()}:
32
+ raise ValueError("Trainable modules do not belong to the classifier")
33
+ if any(
34
+ id(parameter) in selected for parameter in model.model.final_norm.parameters()
35
+ ):
36
+ raise ValueError("Encoder final normalization must remain frozen")
37
+ model.requires_grad_(False)
38
+ for module in modules:
39
+ module.requires_grad_(True)
40
+ tensors = {
41
+ name: {"shape": list(parameter.shape), "numel": parameter.numel()}
42
+ for name, parameter in named.items()
43
+ if parameter.requires_grad
44
+ }
45
+ return {
46
+ "architecture": type(model).__name__,
47
+ "layer_indices": list(range(start, len(layers))),
48
+ "trainable_tensors": tensors,
49
+ "trainable_parameters": sum(item["numel"] for item in tensors.values()),
50
+ "encoder_final_norm_trainable": False,
51
+ }
52
+
53
+
54
+ def frozen_parameter_receipt(model):
55
+ """Hash frozen parameters once so an optional restricted update is auditable."""
56
+ return {
57
+ name: {
58
+ "shape": list(parameter.shape),
59
+ "dtype": str(parameter.dtype),
60
+ "sha256": tensor_digest(parameter),
61
+ }
62
+ for name, parameter in model.named_parameters()
63
+ if not parameter.requires_grad
64
+ }
65
+
66
+
67
+ def assert_frozen_parameters_preserved(expected, model):
68
+ if frozen_parameter_receipt(model) != expected:
69
+ raise ValueError("Frozen parameter scope or tensor values changed")
70
+
71
+
72
+ def validate_method(method, adapter, fresh_head):
73
+ if method not in {"lora", "full"}:
74
+ raise ValueError("Training method must be lora or full")
75
+ if method == "lora" and (adapter is None or fresh_head):
76
+ raise ValueError("LoRA continuation requires its initialized adapter")
77
+ if method == "full" and adapter is not None:
78
+ raise ValueError(
79
+ "Full training starts from a complete checkpoint, not an adapter"
80
+ )
81
+
82
+
83
+ def load_trainable_model(base, contract, adapter, method, fresh_head=False):
84
+ validate_method(method, adapter, fresh_head)
85
+ model, tokenizer, labels, ids = load_model(
86
+ base, contract, adapter, trainable=method == "lora"
87
+ )
88
+ if method == "full":
89
+ if type(model).__name__ != "ModernBertForSequenceClassification":
90
+ raise ValueError("Full sequence training requires a ModernBERT classifier")
91
+ model.requires_grad_(True)
92
+ if fresh_head:
93
+ from torch import nn
94
+
95
+ head_parameter = next(model.head.parameters())
96
+ model.head = type(model.head)(model.config).to(
97
+ device=head_parameter.device, dtype=head_parameter.dtype
98
+ )
99
+ classifier = model.classifier
100
+ model.classifier = nn.Linear(
101
+ classifier.in_features,
102
+ classifier.out_features,
103
+ bias=classifier.bias is not None,
104
+ device=classifier.weight.device,
105
+ dtype=classifier.weight.dtype,
106
+ )
107
+ # Fresh modules let HF apply its native initialization without
108
+ # mutating the initialization guards on loaded checkpoint tensors.
109
+ model.head.apply(model._init_weights)
110
+ # ModernBERT initializes the final classifier on its owning task
111
+ # module, not when visiting the bare Linear child.
112
+ model._init_weights(model)
113
+ return model, tokenizer, labels, ids
114
+
115
+
116
+ def optimizer_groups(model, learning_rate, head_learning_rate=None):
117
+ rates = [learning_rate]
118
+ if head_learning_rate is not None:
119
+ rates.append(head_learning_rate)
120
+ if any(not math.isfinite(rate) or rate <= 0 for rate in rates):
121
+ raise ValueError("Learning rates must be finite and positive")
122
+ base = model.get_base_model() if hasattr(model, "get_base_model") else model
123
+ head_ids = {
124
+ id(parameter)
125
+ for module in (base.head, base.classifier)
126
+ for parameter in module.parameters()
127
+ if parameter.requires_grad
128
+ }
129
+ encoder, head = [], []
130
+ for parameter in model.parameters():
131
+ if parameter.requires_grad:
132
+ (head if id(parameter) in head_ids else encoder).append(parameter)
133
+ groups = []
134
+ for name, parameters, rate in (
135
+ ("encoder", encoder, learning_rate),
136
+ ("task_head", head, head_learning_rate or learning_rate),
137
+ ):
138
+ if parameters:
139
+ groups.append(
140
+ {"name": name, "params": parameters, "lr": rate, "initial_lr": rate}
141
+ )
142
+ if not groups:
143
+ raise ValueError("No trainable parameters")
144
+ return groups
145
+
146
+
147
+ def initial_artifact_receipt(base, adapter):
148
+ """Hash the actual local initialization, including every weight shard."""
149
+ root = Path(base)
150
+ if not root.is_dir():
151
+ raise ValueError("Training requires a downloaded local base checkpoint")
152
+ names = {"config.json", "tokenizer.json", "tokenizer_config.json"}
153
+ for path in root.iterdir():
154
+ if (
155
+ path.suffix == ".safetensors"
156
+ or path.name
157
+ in {
158
+ "model.safetensors.index.json",
159
+ "pytorch_model.bin",
160
+ "pytorch_model.bin.index.json",
161
+ }
162
+ or path.name.startswith("pytorch_model-")
163
+ ):
164
+ names.add(path.name)
165
+ files = {}
166
+ for name in sorted(names):
167
+ path = root / name
168
+ if path.is_file():
169
+ with path.open("rb") as stream:
170
+ files[name] = hashlib.file_digest(stream, "sha256").hexdigest()
171
+ result = {"base_files": files}
172
+ if adapter is not None:
173
+ for filename in ("adapter_model.safetensors", "adapter_config.json"):
174
+ path = Path(adapter) / filename
175
+ if filename == "adapter_config.json" and not path.is_file():
176
+ result[filename] = None
177
+ continue
178
+ with path.open("rb") as stream:
179
+ result[filename] = hashlib.file_digest(stream, "sha256").hexdigest()
180
+ return result
181
+
182
+
183
+ def verify_full_checkpoint(model, checkpoint):
184
+ """Require every tensor of a complete sequence checkpoint to be preserved."""
185
+ if type(model).__name__ != "ModernBertForSequenceClassification":
186
+ raise ValueError("Full checkpoints must own the actual encoder weights")
187
+ from safetensors.torch import load_file
188
+
189
+ saved = load_file(str(Path(checkpoint) / "model.safetensors"), device="cpu")
190
+ state = model.state_dict()
191
+ if saved.keys() != state.keys():
192
+ raise ValueError("Full checkpoint omitted or introduced model tensors")
193
+ import torch
194
+
195
+ for name, value in state.items():
196
+ if saved[name].dtype != value.dtype or not torch.equal(
197
+ saved[name], value.detach().cpu()
198
+ ):
199
+ raise ValueError(f"Full checkpoint changed tensor: {name}")
200
+
201
+
202
+ def save_training_checkpoint(model, tokenizer, destination, method, base_id, revision):
203
+ if method == "lora":
204
+ save_adapter(model, tokenizer, destination, base_id, revision)
205
+ return
206
+ if method != "full" or hasattr(model, "peft_config"):
207
+ raise ValueError("Full checkpoints must own the actual encoder weights")
208
+ destination.mkdir(parents=True, exist_ok=True)
209
+ expected = task_head_scope(model)
210
+ model.save_pretrained(destination, safe_serialization=True)
211
+ verify_full_checkpoint(model, destination)
212
+ tokenizer.save_pretrained(destination)
213
+ (destination / "task-head.json").write_text(json.dumps(expected, indent=2) + "\n")
214
+ (destination / "training-origin.json").write_text(
215
+ json.dumps(
216
+ {"method": method, "base_model": base_id, "base_revision": revision},
217
+ indent=2,
218
+ )
219
+ + "\n"
220
+ )
reproducibility/sequence_repair/prepare_context.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Frozen context curriculum and held-out stress variants from task source groups."""
2
+
3
+ # Chinese punctuation is intentional in language-specific task markers.
4
+ # ruff: noqa: PLC0415, RUF001
5
+ import argparse
6
+ import hashlib
7
+ import json
8
+ from collections import defaultdict
9
+ from pathlib import Path
10
+
11
+ from .context import insert_payload
12
+ from .corpus import write_corpus
13
+ from .data import read_records
14
+
15
+ LANGUAGE_ALIASES = {
16
+ "ara": "ar",
17
+ "eng": "en",
18
+ "zho": "zh",
19
+ "spa": "es",
20
+ "fra": "fr",
21
+ "deu": "de",
22
+ "jpn": "ja",
23
+ }
24
+
25
+ TASK_MARKERS = {
26
+ "ar": "المهمة المطلوبة:\n",
27
+ "de": "Angefragte Aufgabe:\n",
28
+ "en": "Requested task:\n",
29
+ "es": "Tarea solicitada:\n",
30
+ "fr": "Tâche demandée :\n",
31
+ "ja": "依頼されたタスク:\n",
32
+ "zh": "需要完成的任务:\n",
33
+ }
34
+
35
+
36
+ def task_payload(row):
37
+ """Mark a task in its declared language without rewriting its source text."""
38
+ language = LANGUAGE_ALIASES.get(row["language"], row["language"])
39
+ if language not in TASK_MARKERS:
40
+ raise ValueError(f"No context task marker for language: {language}")
41
+ return TASK_MARKERS[language] + row["text"]
42
+
43
+
44
+ def choose_payloads(rows, languages, per_label):
45
+ pools = defaultdict(list)
46
+ for row in rows:
47
+ language = LANGUAGE_ALIASES.get(row.get("language"), row.get("language"))
48
+ if language in languages:
49
+ pools[(language, row["label"])].append(row)
50
+ result = []
51
+ for (language, _label), pool in sorted(pools.items()):
52
+ seen = set()
53
+ for row in sorted(
54
+ pool, key=lambda item: hashlib.sha256(item["id"].encode()).hexdigest()
55
+ ):
56
+ if row["group_id"] in seen:
57
+ continue
58
+ seen.add(row["group_id"])
59
+ result.append({**row, "language": language})
60
+ if len(seen) >= per_label:
61
+ break
62
+ return result
63
+
64
+
65
+ def main():
66
+ from transformers import AutoTokenizer
67
+
68
+ parser = argparse.ArgumentParser()
69
+ parser.add_argument("--corpus", type=Path, required=True)
70
+ parser.add_argument("--tokenizer", required=True)
71
+ parser.add_argument("--backgrounds", type=Path, required=True)
72
+ parser.add_argument("--output", type=Path, required=True)
73
+ parser.add_argument("--train-per-label", type=int, default=3)
74
+ parser.add_argument("--eval-per-label", type=int, default=1)
75
+ parser.add_argument(
76
+ "--budgets", type=int, nargs="+", default=[4096, 8192, 16384, 32768]
77
+ )
78
+ parser.add_argument(
79
+ "--languages", nargs="+", choices=sorted(TASK_MARKERS), default=["en", "zh"]
80
+ )
81
+ parser.add_argument("--payload-sources", nargs="+")
82
+ args = parser.parse_args()
83
+ if min(args.train_per_label, args.eval_per_label, *args.budgets) <= 0:
84
+ raise ValueError("Context budgets and sample counts must be positive")
85
+ backgrounds = json.loads(args.backgrounds.read_text())
86
+ if len({text for group in backgrounds.values() for text in group.values()}) != sum(
87
+ len(group) for group in backgrounds.values()
88
+ ):
89
+ raise ValueError("Background text must differ across languages and partitions")
90
+ tokenizer = AutoTokenizer.from_pretrained(args.tokenizer)
91
+ contract = json.loads((args.corpus / "contract.json").read_text())
92
+
93
+ def count(text):
94
+ return len(tokenizer(text, truncation=False, padding=False)["input_ids"])
95
+
96
+ splits = {}
97
+ for split in ["train", "dev", "test"]:
98
+ rows = read_records([args.corpus / f"{split}.jsonl"], contract["label2id"])
99
+ if args.payload_sources:
100
+ rows = [row for row in rows if row.get("source") in args.payload_sources]
101
+ chosen = choose_payloads(
102
+ rows,
103
+ args.languages,
104
+ args.train_per_label if split == "train" else args.eval_per_label,
105
+ )
106
+ if not chosen:
107
+ raise ValueError(f"No eligible {split} source groups")
108
+ output = []
109
+ for row in chosen:
110
+ language = row["language"]
111
+ payload = task_payload(row)
112
+ for budget in args.budgets:
113
+ for position in ["head", "middle", "tail"]:
114
+ built = insert_payload(
115
+ payload, backgrounds[split][language], position, budget, count
116
+ )
117
+ output.append(
118
+ {
119
+ **row,
120
+ "id": f"context:{row['id']}:{budget}:{position}",
121
+ "text": built["text"],
122
+ "source": "authored-context-on-frozen-source",
123
+ "parent_source": row["source"],
124
+ "parent_id": row["id"],
125
+ "length_bucket": str(budget),
126
+ "position": position,
127
+ "actual_tokens": built["actual_tokens"],
128
+ "payload_start": built["payload_start"],
129
+ "payload_end": built["payload_end"],
130
+ }
131
+ )
132
+ splits[split] = output
133
+ print(
134
+ json.dumps(
135
+ {"split": split, "payloads": len(chosen), "variants": len(output)}
136
+ ),
137
+ flush=True,
138
+ )
139
+ provenance = {
140
+ "parent_manifest_sha256": hashlib.sha256(
141
+ (args.corpus / "manifest.json").read_bytes()
142
+ ).hexdigest(),
143
+ "backgrounds_sha256": hashlib.sha256(args.backgrounds.read_bytes()).hexdigest(),
144
+ "tokenizer_sha256": hashlib.sha256(
145
+ (Path(args.tokenizer) / "tokenizer.json").read_bytes()
146
+ ).hexdigest(),
147
+ "train_payloads_per_label_language": args.train_per_label,
148
+ "eval_payloads_per_label_language": args.eval_per_label,
149
+ "budgets": args.budgets,
150
+ "languages": args.languages,
151
+ "scope": "Authored repeated multi-paragraph context around frozen source requests, not natural long-document accuracy. Label is the explicitly marked requested task. Paired length/position variants retain the parent source group and partition.",
152
+ "test_used": False,
153
+ }
154
+ print(json.dumps(write_corpus(args.output, splits, contract, provenance), indent=2))
155
+
156
+
157
+ if __name__ == "__main__":
158
+ main()
reproducibility/sequence_repair/prepare_retention.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Cache complete FP32 teacher outputs for explicitly marked replay TRAIN rows."""
2
+
3
+ # ruff: noqa: PLC0415
4
+
5
+ import argparse
6
+ import hashlib
7
+ import json
8
+ from pathlib import Path
9
+
10
+ from .data import read_records
11
+ from .model import load_model
12
+ from .optimization import initial_artifact_receipt, verify_full_checkpoint
13
+ from .retention import row_identity, tokenizer_file_receipt, write_retention_cache
14
+
15
+
16
+ def main():
17
+ import torch
18
+
19
+ parser = argparse.ArgumentParser(description=__doc__)
20
+ parser.add_argument("--base", required=True)
21
+ parser.add_argument("--contract", required=True)
22
+ parser.add_argument("--train", nargs="+", required=True)
23
+ parser.add_argument("--output", type=Path, required=True)
24
+ parser.add_argument("--max-length", type=int, default=32768)
25
+ parser.add_argument("--device", default="cuda")
26
+ args = parser.parse_args()
27
+ if args.output.exists():
28
+ raise ValueError("Refusing to overwrite retention targets")
29
+ if args.max_length <= 0:
30
+ raise ValueError("The teacher input budget must be positive")
31
+ initialization = initial_artifact_receipt(args.base, None)
32
+ model, tokenizer, labels, _ = load_model(args.base, args.contract)
33
+ if model.config.problem_type != "single_label_classification":
34
+ raise ValueError("Retention targets require single-label classification")
35
+ verify_full_checkpoint(model, args.base)
36
+ if args.max_length > model.config.max_position_embeddings:
37
+ raise ValueError("Teacher input budget exceeds checkpoint capacity")
38
+ rows = read_records(args.train, labels)
39
+ if any(row.get("split", "train") != "train" for row in rows):
40
+ raise ValueError("Teacher targets must not use development or test rows")
41
+ if any(not isinstance(row.get("retention_replay", False), bool) for row in rows):
42
+ raise ValueError("retention_replay must be a boolean TRAIN annotation")
43
+ replay = [row for row in rows if row.get("retention_replay", False)]
44
+ if not replay:
45
+ raise ValueError("Mark the old TRAIN rows with retention_replay: true")
46
+ encoded = [
47
+ tokenizer(row["text"], truncation=False, padding=False) for row in replay
48
+ ]
49
+ if any(len(value["input_ids"]) > args.max_length for value in encoded):
50
+ raise ValueError(
51
+ "A replay row exceeds the teacher budget; truncation is forbidden"
52
+ )
53
+ torch.set_num_threads(8)
54
+ torch.set_float32_matmul_precision("highest")
55
+ torch.backends.cuda.matmul.allow_tf32 = False
56
+ torch.backends.cudnn.allow_tf32 = False
57
+ model = model.to(args.device).eval()
58
+ records = []
59
+ with torch.inference_mode():
60
+ for row, value in zip(replay, encoded, strict=True):
61
+ batch = {
62
+ name: torch.tensor([ids], device=args.device)
63
+ for name, ids in value.items()
64
+ }
65
+ logits = model(**batch).logits
66
+ if logits.dtype != torch.float32 or not bool(torch.isfinite(logits).all()):
67
+ raise ValueError("Teacher outputs must be finite native FP32 logits")
68
+ records.append(
69
+ {**row_identity(row, value), "logits": logits[0].cpu().tolist()}
70
+ )
71
+ manifest = write_retention_cache(
72
+ args.output,
73
+ initialization=initialization,
74
+ contract_sha256=hashlib.sha256(Path(args.contract).read_bytes()).hexdigest(),
75
+ tokenizer_files=tokenizer_file_receipt(args.base),
76
+ label_to_id=labels,
77
+ records=records,
78
+ )
79
+ print(
80
+ json.dumps(
81
+ {"manifest": str(manifest), "rows": len(records), "dtype": "float32"}
82
+ )
83
+ )
84
+
85
+
86
+ if __name__ == "__main__":
87
+ main()
reproducibility/sequence_repair/retention.py ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Optional, identity-bound TRAIN replay targets and categorical retention loss."""
2
+
3
+ # Torch stays optional for cache identity and data checks.
4
+ # ruff: noqa: PLC0415
5
+
6
+ import hashlib
7
+ import json
8
+ import math
9
+ import re
10
+ import struct
11
+ from collections.abc import Mapping
12
+ from pathlib import Path
13
+
14
+ _LOGIT_RANK = 2
15
+ _MINIMUM_LABELS = 2
16
+
17
+
18
+ def masked_teacher_kl(
19
+ logits, teacher_logits, eligible, *, examples_per_step, temperature=2.0
20
+ ):
21
+ """Return detached T² KL(teacher || student), summed over a logical batch."""
22
+ import torch
23
+ from torch.nn import functional
24
+
25
+ if type(examples_per_step) is not int or examples_per_step <= 0:
26
+ raise ValueError("Logical example count must be a positive integer")
27
+ if (
28
+ isinstance(temperature, bool)
29
+ or not isinstance(temperature, (int, float))
30
+ or not math.isfinite(temperature)
31
+ or temperature <= 0
32
+ ):
33
+ raise ValueError("Retention temperature must be finite and positive")
34
+ if (
35
+ logits.ndim != _LOGIT_RANK
36
+ or logits.shape[1] < _MINIMUM_LABELS
37
+ or logits.shape[0] > examples_per_step
38
+ or teacher_logits.shape != logits.shape
39
+ or eligible.shape != logits.shape[:1]
40
+ or eligible.dtype != torch.bool
41
+ or not logits.is_floating_point()
42
+ or not teacher_logits.is_floating_point()
43
+ or teacher_logits.device != logits.device
44
+ or eligible.device != logits.device
45
+ or not torch.isfinite(logits).all()
46
+ ):
47
+ raise ValueError(
48
+ "Retention logits and mask must have compatible finite geometry"
49
+ )
50
+ if not eligible.any():
51
+ # Connected zero without summing possibly huge finite masked logits.
52
+ return logits.float()[:0].sum()
53
+ student = logits[eligible].float() / temperature
54
+ teacher = teacher_logits.detach()[eligible].float() / temperature
55
+ if not torch.isfinite(teacher).all():
56
+ raise ValueError("Eligible teacher logits must be finite")
57
+ student_logp = functional.log_softmax(student, dim=-1)
58
+ teacher_logp = functional.log_softmax(teacher, dim=-1)
59
+ loss = functional.kl_div(
60
+ student_logp, teacher_logp, log_target=True, reduction="sum"
61
+ ) * (temperature**2 / examples_per_step)
62
+ if not torch.isfinite(loss):
63
+ raise ValueError("Retention loss must be finite")
64
+ return loss
65
+
66
+
67
+ def _sha(path):
68
+ with Path(path).open("rb") as stream:
69
+ return hashlib.file_digest(stream, "sha256").hexdigest()
70
+
71
+
72
+ def _digest_map(value):
73
+ if not isinstance(value, dict) or not value:
74
+ raise ValueError("Artifact receipts must contain file digests")
75
+ for name, digest in value.items():
76
+ if (
77
+ not isinstance(name, str)
78
+ or not name
79
+ or Path(name).is_absolute()
80
+ or ".." in Path(name).parts
81
+ or not isinstance(digest, str)
82
+ or re.fullmatch(r"[0-9a-f]{64}", digest) is None
83
+ ):
84
+ raise ValueError(
85
+ "Artifact receipt contains an invalid relative name or SHA"
86
+ )
87
+
88
+
89
+ def tokenizer_file_receipt(base):
90
+ """Bind the complete local fast-tokenizer files used by ModernBERT."""
91
+ base = Path(base)
92
+ names = {
93
+ "tokenizer.json",
94
+ "tokenizer_config.json",
95
+ "special_tokens_map.json",
96
+ "added_tokens.json",
97
+ "vocab.json",
98
+ "vocab.txt",
99
+ "merges.txt",
100
+ "tokenizer.model",
101
+ "spiece.model",
102
+ "sentencepiece.bpe.model",
103
+ "chat_template.jinja",
104
+ }
105
+ names.update(
106
+ str(path.relative_to(base))
107
+ for path in (base / "chat_templates").glob("*.jinja")
108
+ )
109
+ result = {
110
+ name: _sha(base / name) for name in sorted(names) if (base / name).is_file()
111
+ }
112
+ if not {"tokenizer.json", "tokenizer_config.json"} <= result.keys():
113
+ raise ValueError("Retention requires the complete local fast tokenizer")
114
+ return result
115
+
116
+
117
+ def row_identity(row, encoded):
118
+ """Bind raw text and every unpadded model input, without storing text in targets."""
119
+ if any(
120
+ not isinstance(row.get(key), str) or not row[key]
121
+ for key in ("id", "group_id", "text", "label")
122
+ ):
123
+ raise ValueError("Retention rows require string ID, group, text and label")
124
+ if (
125
+ not isinstance(encoded, Mapping)
126
+ or not {"input_ids", "attention_mask"} <= encoded.keys()
127
+ ):
128
+ raise ValueError("Retention requires complete unpadded tokenizer inputs")
129
+ encoded = dict(encoded)
130
+ if not encoded.keys() <= {
131
+ "input_ids",
132
+ "attention_mask",
133
+ "token_type_ids",
134
+ "position_ids",
135
+ }:
136
+ raise ValueError("Unexpected tokenizer input in retention identity")
137
+ if not isinstance(encoded["input_ids"], list):
138
+ raise ValueError("Retention input IDs must be an unpadded integer list")
139
+ size = len(encoded["input_ids"])
140
+ if (
141
+ not size
142
+ or any(
143
+ not isinstance(values, list)
144
+ or len(values) != size
145
+ or any(type(value) is not int or value < 0 for value in values)
146
+ for values in encoded.values()
147
+ )
148
+ or any(value != 1 for value in encoded["attention_mask"])
149
+ ):
150
+ raise ValueError("Retention inputs must be complete unpadded integer sequences")
151
+ token_bytes = json.dumps(encoded, sort_keys=True, separators=(",", ":")).encode()
152
+ return {
153
+ "id": row["id"],
154
+ "group_id": row["group_id"],
155
+ "label": row["label"],
156
+ "text_sha256": hashlib.sha256(row["text"].encode()).hexdigest(),
157
+ "tokens_sha256": hashlib.sha256(token_bytes).hexdigest(),
158
+ }
159
+
160
+
161
+ def _labels(value):
162
+ if (
163
+ not isinstance(value, dict)
164
+ or len(value) < _MINIMUM_LABELS
165
+ or any(
166
+ not isinstance(name, str) or not name or type(index) is not int
167
+ for name, index in value.items()
168
+ )
169
+ or sorted(value.values()) != list(range(len(value)))
170
+ ):
171
+ raise ValueError("Retention labels must be a contiguous bijection")
172
+
173
+
174
+ def _record(record, labels):
175
+ expected = {"id", "group_id", "label", "text_sha256", "tokens_sha256", "logits"}
176
+ if not isinstance(record, dict) or set(record) != expected:
177
+ raise ValueError("Retention target fields differ from the schema")
178
+ if any(
179
+ not isinstance(record[key], str) or not record[key]
180
+ for key in ("id", "group_id", "label")
181
+ ):
182
+ raise ValueError("Invalid retention target identity")
183
+ _digest_map({key: record[key] for key in ("text_sha256", "tokens_sha256")})
184
+ values = record["logits"]
185
+ if (
186
+ record["label"] not in labels
187
+ or not isinstance(values, list)
188
+ or len(values) != len(labels)
189
+ ):
190
+ raise ValueError("Retention logits differ from the label geometry")
191
+ for value in values:
192
+ if (
193
+ isinstance(value, bool)
194
+ or not isinstance(value, (int, float))
195
+ or not math.isfinite(value)
196
+ ):
197
+ raise ValueError("Cached teacher logits must be finite float32 values")
198
+ try:
199
+ exact = struct.unpack("f", struct.pack("f", value))[0]
200
+ except (OverflowError, struct.error) as error:
201
+ raise ValueError("Cached teacher logit exceeds float32") from error
202
+ if exact != value:
203
+ raise ValueError("Cached teacher logit was not serialized from float32")
204
+
205
+
206
+ def _metadata(initialization, contract_sha256, tokenizer_files, label_to_id):
207
+ if not isinstance(initialization, dict) or set(initialization) != {"base_files"}:
208
+ raise ValueError("Retention requires an exact complete-checkpoint receipt")
209
+ _digest_map(initialization["base_files"])
210
+ _digest_map({"contract": contract_sha256})
211
+ _digest_map(tokenizer_files)
212
+ _labels(label_to_id)
213
+ for name, digest in tokenizer_files.items():
214
+ if (
215
+ name in initialization["base_files"]
216
+ and initialization["base_files"][name] != digest
217
+ ):
218
+ raise ValueError("Tokenizer and initialization receipts disagree")
219
+
220
+
221
+ def write_retention_cache(
222
+ destination,
223
+ *,
224
+ initialization,
225
+ contract_sha256,
226
+ tokenizer_files,
227
+ label_to_id,
228
+ records,
229
+ ):
230
+ """Write a new portable cache; the loader separately verifies admitted TRAIN scope."""
231
+ _metadata(initialization, contract_sha256, tokenizer_files, label_to_id)
232
+ records = list(records)
233
+ for record in records:
234
+ _record(record, label_to_id)
235
+ records.sort(key=lambda record: record["id"])
236
+ ids = [record["id"] for record in records]
237
+ if not ids or len(ids) != len(set(ids)):
238
+ raise ValueError("Retention targets must be nonempty with unique IDs")
239
+ destination = Path(destination)
240
+ destination.mkdir(parents=True, exist_ok=False)
241
+ targets = destination / "targets.jsonl"
242
+ with targets.open("w") as stream:
243
+ for record in records:
244
+ stream.write(json.dumps(record, sort_keys=True, allow_nan=False) + "\n")
245
+ manifest = {
246
+ "version": 1,
247
+ "split": "train",
248
+ "dtype": "float32",
249
+ "initialization": initialization,
250
+ "contract_sha256": contract_sha256,
251
+ "tokenizer_files": tokenizer_files,
252
+ "label_to_id": label_to_id,
253
+ "replay_ids": ids,
254
+ "rows": len(records),
255
+ "records_file": targets.name,
256
+ "records_sha256": _sha(targets),
257
+ }
258
+ path = destination / "manifest.json"
259
+ path.write_text(json.dumps(manifest, indent=2, allow_nan=False) + "\n")
260
+ return path
261
+
262
+
263
+ class RetentionCache:
264
+ """Targets for explicitly designated old TRAIN rows; unknown rows are rejected."""
265
+
266
+ @classmethod
267
+ def load(
268
+ cls,
269
+ path,
270
+ *,
271
+ initialization,
272
+ contract_sha256,
273
+ tokenizer_files,
274
+ train_rows,
275
+ tokenized_rows,
276
+ label_to_id,
277
+ replay_ids,
278
+ ):
279
+ _metadata(initialization, contract_sha256, tokenizer_files, label_to_id)
280
+ path = Path(path)
281
+ raw = path.read_bytes()
282
+ manifest = json.loads(raw)
283
+ expected = {
284
+ "version",
285
+ "split",
286
+ "dtype",
287
+ "initialization",
288
+ "contract_sha256",
289
+ "tokenizer_files",
290
+ "label_to_id",
291
+ "replay_ids",
292
+ "rows",
293
+ "records_file",
294
+ "records_sha256",
295
+ }
296
+ if not isinstance(manifest, dict) or set(manifest) != expected:
297
+ raise ValueError("Retention manifest fields differ from the schema")
298
+ if (
299
+ type(manifest["version"]) is not int
300
+ or manifest["version"] != 1
301
+ or manifest["split"] != "train"
302
+ or manifest["dtype"] != "float32"
303
+ ):
304
+ raise ValueError("Retention requires version1 float32 TRAIN targets")
305
+ for key, value in (
306
+ ("initialization", initialization),
307
+ ("contract_sha256", contract_sha256),
308
+ ("tokenizer_files", tokenizer_files),
309
+ ("label_to_id", label_to_id),
310
+ ):
311
+ if manifest[key] != value:
312
+ raise ValueError(f"Retention cache differs from the actual {key}")
313
+ identities, marked = {}, set()
314
+ for row in train_rows:
315
+ if row.get("split", "train") != "train":
316
+ raise ValueError(
317
+ "Retention targets cannot include development or test rows"
318
+ )
319
+ if type(row.get("retention_replay", False)) is not bool:
320
+ raise ValueError("TRAIN retention_replay designation must be boolean")
321
+ if row["id"] in identities or row["id"] not in tokenized_rows:
322
+ raise ValueError("Missing tokens or duplicate TRAIN retention ID")
323
+ identities[row["id"]] = row_identity(row, tokenized_rows[row["id"]])
324
+ if row["label"] not in label_to_id:
325
+ raise ValueError("Unknown TRAIN retention label")
326
+ if row.get("retention_replay", False):
327
+ marked.add(row["id"])
328
+ replay_ids = list(replay_ids)
329
+ if (
330
+ not marked
331
+ or len(replay_ids) != len(set(replay_ids))
332
+ or set(replay_ids) != marked
333
+ or manifest["replay_ids"] != sorted(marked)
334
+ ):
335
+ raise ValueError("Cache must cover exactly the designated old TRAIN subset")
336
+ filename = manifest["records_file"]
337
+ if (
338
+ not isinstance(filename, str)
339
+ or Path(filename).name != filename
340
+ or filename in {"", ".", ".."}
341
+ ):
342
+ raise ValueError("Retention records must use a relative filename")
343
+ targets = path.parent / filename
344
+ data = targets.read_bytes()
345
+ if hashlib.sha256(data).hexdigest() != manifest["records_sha256"]:
346
+ raise ValueError("Retention target bytes changed")
347
+ records = [json.loads(line) for line in data.splitlines() if line.strip()]
348
+ if (
349
+ type(manifest["rows"]) is not int
350
+ or len(records) != manifest["rows"]
351
+ or len(records) != len(marked)
352
+ ):
353
+ raise ValueError("Retention target coverage is incomplete")
354
+ cached = {}
355
+ for record in records:
356
+ _record(record, label_to_id)
357
+ identity = {key: value for key, value in record.items() if key != "logits"}
358
+ if (
359
+ record["id"] not in marked
360
+ or record["id"] in cached
361
+ or identity != identities[record["id"]]
362
+ ):
363
+ raise ValueError(
364
+ "Retention target does not match its old TRAIN identity"
365
+ )
366
+ values = record["logits"]
367
+ maximum = max(values)
368
+ eligible = (
369
+ values.count(maximum) == 1
370
+ and values.index(maximum) == label_to_id[record["label"]]
371
+ )
372
+ cached[record["id"]] = (values, eligible)
373
+ result = cls()
374
+ result._identities, result._cached, result._width = (
375
+ identities,
376
+ cached,
377
+ len(label_to_id),
378
+ )
379
+ result.receipt = {
380
+ "manifest_sha256": hashlib.sha256(raw).hexdigest(),
381
+ "records_sha256": manifest["records_sha256"],
382
+ "replay_rows": len(cached),
383
+ "eligible_rows": sum(eligible for _, eligible in cached.values()),
384
+ }
385
+ return result
386
+
387
+ def targets(self, rows, device):
388
+ import torch
389
+
390
+ values, masks = [], []
391
+ for row in rows:
392
+ identity = self._identities.get(row["id"])
393
+ if (
394
+ identity is None
395
+ or any(row[key] != identity[key] for key in ("id", "group_id", "label"))
396
+ or hashlib.sha256(row["text"].encode()).hexdigest()
397
+ != identity["text_sha256"]
398
+ ):
399
+ raise ValueError(
400
+ "Retention batch is not part of the validated TRAIN corpus"
401
+ )
402
+ logits, eligible = self._cached.get(row["id"], ([0.0] * self._width, False))
403
+ values.append(logits)
404
+ masks.append(eligible)
405
+ return (
406
+ torch.tensor(values, dtype=torch.float32, device=device).reshape(
407
+ len(rows), self._width
408
+ ),
409
+ torch.tensor(masks, dtype=torch.bool, device=device),
410
+ )
reproducibility/sequence_repair/runtime_mapping.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Serialize classifier label sidecars consumed by the router's Go loaders."""
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ RUNTIME_TASKS = (
7
+ "domain",
8
+ "fact-check",
9
+ "modality",
10
+ "feedback",
11
+ "prompt-guard",
12
+ "safety",
13
+ "hazard",
14
+ )
15
+
16
+
17
+ def write_runtime_mappings(destination, labels, task):
18
+ """Write both HF and router layouts from one contiguous label bijection."""
19
+ if task not in RUNTIME_TASKS:
20
+ raise ValueError("Unknown runtime classifier task")
21
+ if not labels or set(labels.values()) != set(range(len(labels))):
22
+ raise ValueError("Runtime label IDs must be a contiguous bijection")
23
+ if task == "modality" and labels != {"AR": 0, "DIFFUSION": 1, "BOTH": 2}:
24
+ raise ValueError("Modality must preserve the native AR/DIFFUSION/BOTH IDs")
25
+ if task == "fact-check" and set(labels) != {
26
+ "NO_FACT_CHECK_NEEDED",
27
+ "FACT_CHECK_NEEDED",
28
+ }:
29
+ raise ValueError("FactCheck requires the router's two semantic labels")
30
+ reverse = {str(index): label for label, index in labels.items()}
31
+ generic = {
32
+ "label2id": labels,
33
+ "id2label": reverse,
34
+ "label_to_idx": labels,
35
+ "idx_to_label": reverse,
36
+ }
37
+ files = {"label_mapping.json": generic}
38
+ if task == "domain":
39
+ files["category_mapping.json"] = {
40
+ "category_to_idx": labels,
41
+ "idx_to_category": reverse,
42
+ }
43
+ sidecars = {
44
+ "fact-check": "fact_check_mapping.json",
45
+ "modality": "modality_mapping.json",
46
+ "prompt-guard": "jailbreak_type_mapping.json",
47
+ }
48
+ if task in sidecars:
49
+ files[sidecars[task]] = {
50
+ "label_to_idx": labels,
51
+ "idx_to_label": reverse,
52
+ }
53
+ for name, data in files.items():
54
+ path = Path(destination) / name
55
+ if path.exists():
56
+ raise ValueError("Refusing to overwrite an existing runtime mapping")
57
+ path.write_text(json.dumps(data, indent=2) + "\n")
58
+ return sorted(files)
reproducibility/sequence_repair/selection.py ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Development-only binary operating points under an explicit false-positive budget."""
2
+
3
+ import hashlib
4
+ import json
5
+ import math
6
+ from collections import Counter, defaultdict
7
+ from fractions import Fraction
8
+
9
+ BINARY_FP_SELECTION = "binary-fp-budget-recall"
10
+ BINARY_LABEL_COUNT = 2
11
+
12
+
13
+ def validate_binary_selection(label_to_id, positive_label, budget):
14
+ """Require an unambiguous binary ontology and a finite, explicit FP budget."""
15
+ if (
16
+ len(label_to_id) != BINARY_LABEL_COUNT
17
+ or set(label_to_id.values()) != {0, 1}
18
+ or any(type(index) is not int for index in label_to_id.values())
19
+ ):
20
+ raise ValueError("FP-budget selection requires exactly two label IDs: 0 and 1")
21
+ if positive_label not in label_to_id:
22
+ raise ValueError("FP-budget selection requires an exact positive label")
23
+ if (
24
+ isinstance(budget, bool)
25
+ or not isinstance(budget, (int, float))
26
+ or not math.isfinite(budget)
27
+ or not 0 <= budget <= 1
28
+ ):
29
+ raise ValueError("Selection false-positive budget must be finite in [0, 1]")
30
+
31
+
32
+ def validate_selection_options(selection, label_to_id, positive_label, budget):
33
+ if selection == BINARY_FP_SELECTION:
34
+ validate_binary_selection(label_to_id, positive_label, budget)
35
+ elif positive_label is not None or budget is not None:
36
+ raise ValueError("Binary selection options require binary-fp-budget-recall")
37
+
38
+
39
+ def binary_selection_support(records, label_to_id, positive_label):
40
+ counts = Counter(row["label"] for row in records)
41
+ if set(counts) != set(label_to_id):
42
+ raise ValueError("FP-budget development data must observe both binary labels")
43
+ if any(not row.get("id") for row in records) or len(
44
+ {row["id"] for row in records}
45
+ ) != len(records):
46
+ raise ValueError("Development IDs must be nonempty and unique")
47
+ return {
48
+ "positive": counts[positive_label],
49
+ "negative": len(records) - counts[positive_label],
50
+ }
51
+
52
+
53
+ def binary_checkpoint_key(operating_point):
54
+ """Tie policy also applies across checkpoints; exact ties keep the earlier step."""
55
+ if not operating_point["feasible"]:
56
+ return None
57
+ return (
58
+ operating_point["recall"],
59
+ -operating_point["confusion"]["fp"],
60
+ operating_point["threshold"],
61
+ )
62
+
63
+
64
+ def fit_binary_operating_point(
65
+ records, predictions, label_to_id, positive_label, budget
66
+ ):
67
+ """Sweep complete tied-score groups with p(positive) >= threshold in [0, 1]."""
68
+ validate_binary_selection(label_to_id, positive_label, budget)
69
+ support = binary_selection_support(records, label_to_id, positive_label)
70
+ by_id = {row["id"]: row for row in predictions}
71
+ if len(by_id) != len(predictions) or set(by_id) != {row["id"] for row in records}:
72
+ raise ValueError("Development prediction IDs must match each row exactly once")
73
+ positive_id = label_to_id[positive_label]
74
+ grouped = defaultdict(lambda: [0, 0])
75
+ evidence = []
76
+ for row in records:
77
+ scores = by_id[row["id"]]["probabilities"]
78
+ if (
79
+ len(scores) != BINARY_LABEL_COUNT
80
+ or any(
81
+ isinstance(score, bool)
82
+ or not isinstance(score, (int, float))
83
+ or not math.isfinite(score)
84
+ or not 0 <= score <= 1
85
+ for score in scores
86
+ )
87
+ or not math.isclose(sum(scores), 1.0, rel_tol=0, abs_tol=1e-5)
88
+ ):
89
+ raise ValueError("Expected finite normalized binary probabilities")
90
+ score = scores[positive_id]
91
+ is_positive = row["label"] == positive_label
92
+ grouped[score][0 if is_positive else 1] += 1
93
+ evidence.append(
94
+ {"id": row["id"], "label": row["label"], "probabilities": scores}
95
+ )
96
+ # Decimal budget arithmetic avoids floor(0.3 * 10) rounding surprises without
97
+ # an epsilon that could admit an extra false positive.
98
+ maximum_fp = math.floor(Fraction(str(budget)) * support["negative"])
99
+ receipt = {
100
+ "metric": BINARY_FP_SELECTION,
101
+ "positive_label": positive_label,
102
+ "positive_label_id": positive_id,
103
+ "negative_label": next(
104
+ label for label in label_to_id if label != positive_label
105
+ ),
106
+ "false_positive_budget": budget,
107
+ "maximum_false_positives": maximum_fp,
108
+ "support": support,
109
+ "comparison": "positive_probability >= threshold",
110
+ "threshold_range": [0, 1],
111
+ "tie_break": ["maximum_recall", "fewer_false_positives", "higher_threshold"],
112
+ "evidence_sha256": hashlib.sha256(
113
+ json.dumps(
114
+ sorted(evidence, key=lambda row: row["id"]),
115
+ sort_keys=True,
116
+ separators=(",", ":"),
117
+ allow_nan=False,
118
+ ).encode()
119
+ ).hexdigest(),
120
+ "test_used": False,
121
+ "feasible": False,
122
+ "threshold": None,
123
+ "recall": None,
124
+ "precision": None,
125
+ "false_positive_rate": None,
126
+ "confusion": None,
127
+ }
128
+ best_key, tp, fp = None, 0, 0
129
+ for threshold in sorted({0.0, 1.0, *grouped}, reverse=True):
130
+ positive_count, negative_count = grouped[threshold]
131
+ tp += positive_count
132
+ fp += negative_count
133
+ if fp > maximum_fp:
134
+ continue
135
+ key = (tp, -fp, threshold)
136
+ if best_key is not None and key <= best_key:
137
+ continue
138
+ best_key = key
139
+ receipt.update(
140
+ {
141
+ "feasible": True,
142
+ "threshold": threshold,
143
+ "recall": tp / support["positive"],
144
+ "precision": tp / (tp + fp) if tp + fp else 0.0,
145
+ "false_positive_rate": fp / support["negative"],
146
+ "confusion": {
147
+ "tp": tp,
148
+ "fp": fp,
149
+ "fn": support["positive"] - tp,
150
+ "tn": support["negative"] - fp,
151
+ },
152
+ }
153
+ )
154
+ if not receipt["feasible"]:
155
+ receipt["infeasible_reason"] = (
156
+ "Negative examples at probability 1 exceed the budget even at threshold 1"
157
+ )
158
+ return receipt
reproducibility/sequence_repair/task_head.py ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Inspect the effective task head and verify its adapter/merged storage."""
2
+
3
+ # ruff: noqa: PLC0415
4
+
5
+ import hashlib
6
+
7
+
8
+ def _modules(model):
9
+ base = model.get_base_model() if hasattr(model, "get_base_model") else model
10
+ if type(base).__name__ not in {
11
+ "ModernBertForSequenceClassification",
12
+ "ModernBertForTokenClassification",
13
+ }:
14
+ raise ValueError(
15
+ "Task-head auditing requires a supported ModernBERT task model"
16
+ )
17
+ for name in ("head", "classifier"):
18
+ module = getattr(base, name)
19
+ owned = hasattr(module, "modules_to_save")
20
+ if owned:
21
+ active = module.active_adapter
22
+ if isinstance(active, list):
23
+ if len(active) != 1:
24
+ raise ValueError("Task-head auditing requires one active adapter")
25
+ active = active[0]
26
+ module = module.modules_to_save[active]
27
+ yield name, module, owned
28
+
29
+
30
+ def tensor_digest(tensor):
31
+ import torch
32
+
33
+ value = tensor.detach().cpu().contiguous()
34
+ if not bool(torch.isfinite(value).all()):
35
+ raise ValueError("Task head contains non-finite tensors")
36
+ return hashlib.sha256(value.view(torch.uint8).numpy().tobytes()).hexdigest()
37
+
38
+
39
+ def task_head_scope(model):
40
+ """Describe actual effective tensors, including deliberately inherited heads."""
41
+ tensors = {}
42
+ for name, module, owned in _modules(model):
43
+ trainable = dict(module.named_parameters())
44
+ for key, tensor in module.state_dict().items():
45
+ parameter = trainable.get(key)
46
+ tensors[f"{name}.{key}"] = {
47
+ "shape": list(tensor.shape),
48
+ "dtype": str(tensor.dtype),
49
+ "numel": tensor.numel(),
50
+ "trainable": parameter is not None and parameter.requires_grad,
51
+ "adapter_owned": owned,
52
+ "sha256": tensor_digest(tensor),
53
+ }
54
+ base = model.get_base_model() if hasattr(model, "get_base_model") else model
55
+ return {
56
+ "architecture": type(base).__name__,
57
+ "modules_to_save": (
58
+ model.peft_config["default"].modules_to_save
59
+ if hasattr(model, "get_base_model")
60
+ else None
61
+ ),
62
+ "trainable_task_head_parameters": sum(
63
+ item["numel"] for item in tensors.values() if item["trainable"]
64
+ ),
65
+ "tensors": tensors,
66
+ }
67
+
68
+
69
+ def verify_saved_task_head(model, adapter_path):
70
+ """Reject an adapter that omits or changes an effective trainable task tensor."""
71
+ from safetensors.torch import load_file
72
+
73
+ saved = load_file(str(adapter_path), device="cpu")
74
+ scope = task_head_scope(model)
75
+ for name, item in scope["tensors"].items():
76
+ key = f"base_model.model.{name}"
77
+ value = saved.get(key)
78
+ item["saved_key"] = key if value is not None else None
79
+ if value is None:
80
+ if item["trainable"] or item["adapter_owned"]:
81
+ raise ValueError(f"Adapter omitted effective task tensor: {name}")
82
+ continue
83
+ if (
84
+ list(value.shape) != item["shape"]
85
+ or str(value.dtype) != item["dtype"]
86
+ or tensor_digest(value) != item["sha256"]
87
+ ):
88
+ raise ValueError(f"Adapter changed effective task tensor: {name}")
89
+ return scope
90
+
91
+
92
+ def assert_task_head_preserved(expected, model):
93
+ """Compare tensor identity across PEFT merge and serialized model reload."""
94
+ observed = task_head_scope(model)
95
+ identity = ("shape", "dtype", "sha256")
96
+ expected_tensors = expected["tensors"]
97
+ if expected_tensors.keys() != observed["tensors"].keys():
98
+ raise ValueError("Task-head tensor names changed")
99
+ for name, item in expected_tensors.items():
100
+ if any(item[key] != observed["tensors"][name][key] for key in identity):
101
+ raise ValueError(f"Task-head tensor changed: {name}")
102
+ return observed
reproducibility/sequence_repair/train.py ADDED
@@ -0,0 +1,791 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Explicit single-GPU classifier training with optional development selection."""
2
+
3
+ # ruff: noqa: PLC0415
4
+
5
+ import argparse
6
+ import hashlib
7
+ import json
8
+ import math
9
+ import random
10
+ import time
11
+ from collections import Counter, defaultdict
12
+ from pathlib import Path
13
+
14
+ from .data import assert_disjoint, file_receipts, label_counts, read_records
15
+ from .evaluate import evaluate_records
16
+ from .optimization import (
17
+ assert_frozen_parameters_preserved,
18
+ configure_trainable_tail,
19
+ frozen_parameter_receipt,
20
+ initial_artifact_receipt,
21
+ load_trainable_model,
22
+ optimizer_groups,
23
+ save_training_checkpoint,
24
+ validate_method,
25
+ verify_full_checkpoint,
26
+ )
27
+ from .retention import RetentionCache, masked_teacher_kl, tokenizer_file_receipt
28
+ from .selection import (
29
+ BINARY_FP_SELECTION,
30
+ binary_checkpoint_key,
31
+ binary_selection_support,
32
+ fit_binary_operating_point,
33
+ validate_selection_options,
34
+ )
35
+ from .task_head import task_head_scope
36
+ from .training_order import load_training_order
37
+
38
+
39
+ def microbatch_loss(logits, labels, accumulation_steps):
40
+ from torch.nn import functional
41
+
42
+ if accumulation_steps <= 0:
43
+ raise ValueError("Gradient accumulation must be positive")
44
+ # Each microbatch has the same number of examples. HF Trainer loss-kwargs
45
+ # normalization is intentionally not involved in this explicit loop.
46
+ return (
47
+ functional.cross_entropy(logits.float(), labels, reduction="mean")
48
+ / accumulation_steps
49
+ )
50
+
51
+
52
+ def global_batch_loss(logits, labels, examples_per_step):
53
+ """Normalize variable microbatches by the same sampled optimizer-step size."""
54
+ from torch.nn import functional
55
+
56
+ if examples_per_step <= 0:
57
+ raise ValueError("Optimizer-step example count must be positive")
58
+ return (
59
+ functional.cross_entropy(logits.float(), labels, reduction="sum")
60
+ / examples_per_step
61
+ )
62
+
63
+
64
+ def token_budget_microbatches(indices, lengths, token_budget):
65
+ """Stable length ordering without dropping or duplicating sampled examples."""
66
+ if token_budget <= 0:
67
+ raise ValueError("Microbatch token budget must be positive")
68
+ batches, current = [], []
69
+ for index in sorted(indices, key=lengths.__getitem__):
70
+ length = lengths[index]
71
+ if length > token_budget:
72
+ raise ValueError("A sampled example exceeds the microbatch token budget")
73
+ if current and length * (len(current) + 1) > token_budget:
74
+ batches.append(current)
75
+ current = []
76
+ current.append(index)
77
+ if current:
78
+ batches.append(current)
79
+ return batches
80
+
81
+
82
+ def selection_score(metrics, selection):
83
+ if selection == "macro-f1":
84
+ return metrics["macro_f1"]
85
+ if selection in {
86
+ "length-macro-f1",
87
+ "source-macro-f1",
88
+ "source-present-macro-f1",
89
+ }:
90
+ field = "length_bucket" if selection == "length-macro-f1" else "source"
91
+ metric = (
92
+ "macro_f1_present_labels"
93
+ if selection == "source-present-macro-f1"
94
+ else "macro_f1"
95
+ )
96
+ groups = list(metrics["breakdowns"][field].values())
97
+ if not groups:
98
+ raise ValueError("Grouped selection requires measured groups")
99
+ return sum(group[metric] for group in groups) / len(groups)
100
+ raise ValueError("Unknown development selection metric")
101
+
102
+
103
+ def length_sampling_pools(rows):
104
+ buckets = defaultdict(lambda: defaultdict(list))
105
+ for index, row in enumerate(rows):
106
+ buckets[str(row.get("length_bucket", "unspecified"))][row["label"]].append(
107
+ index
108
+ )
109
+ return [list(labels.values()) for labels in buckets.values()]
110
+
111
+
112
+ def sample_length_balanced(pools, rng, balance_labels):
113
+ bucket = rng.choice(pools)
114
+ pool = (
115
+ rng.choice(bucket)
116
+ if balance_labels
117
+ else [index for label in bucket for index in label]
118
+ )
119
+ return rng.choice(pool)
120
+
121
+
122
+ def source_sampling_pools(rows, balance_lengths):
123
+ sources = defaultdict(lambda: defaultdict(lambda: defaultdict(list)))
124
+ for index, row in enumerate(rows):
125
+ if not row.get("source"):
126
+ raise ValueError("Source-balanced sampling requires explicit sources")
127
+ length = (
128
+ str(row.get("length_bucket", "unspecified")) if balance_lengths else "all"
129
+ )
130
+ sources[row["source"]][length][row["label"]].append(index)
131
+ return [
132
+ [list(labels.values()) for labels in lengths.values()]
133
+ for lengths in sources.values()
134
+ ]
135
+
136
+
137
+ def source_sampling_weights(rows, specification):
138
+ """Resolve explicit probabilities in the same order as source pools."""
139
+ if specification is None:
140
+ return None
141
+ weights = json.loads(specification)
142
+ sources = list(dict.fromkeys(row.get("source") for row in rows))
143
+ if (
144
+ not sources
145
+ or not all(sources)
146
+ or not isinstance(weights, dict)
147
+ or set(weights) != set(sources)
148
+ ):
149
+ raise ValueError("Source weights must name every eligible source exactly")
150
+ values = [weights[source] for source in sources]
151
+ if any(
152
+ isinstance(value, bool)
153
+ or not isinstance(value, (int, float))
154
+ or not math.isfinite(value)
155
+ or value <= 0
156
+ for value in values
157
+ ):
158
+ raise ValueError("Source weights must be finite positive numbers")
159
+ total = sum(values)
160
+ if not math.isfinite(total):
161
+ raise ValueError("Total source weight must be finite")
162
+ return [value / total for value in values]
163
+
164
+
165
+ def sample_source_balanced(pools, rng, balance_labels, source_weights=None):
166
+ source = (
167
+ rng.choice(pools)
168
+ if source_weights is None
169
+ else rng.choices(pools, weights=source_weights, k=1)[0]
170
+ )
171
+ return sample_length_balanced(source, rng, balance_labels)
172
+
173
+
174
+ def sampling_exposure(rows, drawn):
175
+ """Report sampling counts separately from class/length selection metrics."""
176
+ sources = defaultdict(
177
+ lambda: {"rows": 0, "draws": 0, "unique_rows": 0, "label_draws": Counter()}
178
+ )
179
+ for index, row in enumerate(rows):
180
+ item = sources[row.get("source", "unspecified")]
181
+ count = drawn[index]
182
+ item["rows"] += 1
183
+ item["draws"] += count
184
+ item["unique_rows"] += int(count > 0)
185
+ item["label_draws"][row["label"]] += count
186
+ return dict(sources)
187
+
188
+
189
+ def main():
190
+ import torch
191
+
192
+ parser = argparse.ArgumentParser()
193
+ parser.add_argument("--base", required=True)
194
+ parser.add_argument("--base-id", required=True)
195
+ parser.add_argument("--base-revision", required=True)
196
+ parser.add_argument("--adapter", type=Path)
197
+ parser.add_argument("--method", choices=["lora", "full"], default="lora")
198
+ parser.add_argument("--fresh-head", action="store_true")
199
+ parser.add_argument("--contract", required=True)
200
+ parser.add_argument("--train", nargs="+", required=True)
201
+ parser.add_argument("--dev", nargs="+")
202
+ parser.add_argument("--output", type=Path, required=True)
203
+ parser.add_argument("--steps", type=int, default=400)
204
+ parser.add_argument("--batch-size", type=int, default=8)
205
+ parser.add_argument("--accumulate", type=int, default=2)
206
+ parser.add_argument("--max-length", type=int, default=2048)
207
+ parser.add_argument("--learning-rate", type=float, default=2e-5)
208
+ parser.add_argument("--head-learning-rate", type=float)
209
+ parser.add_argument("--trainable-last-layers", type=int)
210
+ parser.add_argument("--retention-targets", type=Path)
211
+ parser.add_argument("--retention-weight", type=float)
212
+ parser.add_argument("--retention-temperature", type=float)
213
+ parser.add_argument("--eval-every", type=int, default=100)
214
+ parser.add_argument(
215
+ "--evaluation-dtype", choices=["bfloat16", "float32"], default="bfloat16"
216
+ )
217
+ parser.add_argument(
218
+ "--selection",
219
+ choices=[
220
+ "macro-f1",
221
+ "length-macro-f1",
222
+ "source-macro-f1",
223
+ "source-present-macro-f1",
224
+ BINARY_FP_SELECTION,
225
+ ],
226
+ default="macro-f1",
227
+ )
228
+ parser.add_argument("--positive-label")
229
+ parser.add_argument("--selection-false-positive-budget", type=float)
230
+ parser.add_argument("--balanced-sampling", action="store_true")
231
+ parser.add_argument("--length-balanced-sampling", action="store_true")
232
+ parser.add_argument("--source-balanced-sampling", action="store_true")
233
+ parser.add_argument(
234
+ "--source-weights", help="JSON object of explicit source weights"
235
+ )
236
+ parser.add_argument("--microbatch-token-budget", type=int)
237
+ parser.add_argument("--training-order", type=Path)
238
+ mode = parser.add_mutually_exclusive_group()
239
+ mode.add_argument("--probe-only", action="store_true")
240
+ mode.add_argument(
241
+ "--train-only",
242
+ action="store_true",
243
+ help="Skip development data and selection; save only the final checkpoint",
244
+ )
245
+ parser.add_argument("--seed", type=int, default=20260913)
246
+ args = parser.parse_args()
247
+ if args.train_only:
248
+ if args.dev:
249
+ parser.error("--train-only must not receive development data")
250
+ if (
251
+ args.selection != "macro-f1"
252
+ or args.positive_label is not None
253
+ or args.selection_false_positive_budget is not None
254
+ ):
255
+ parser.error("--train-only does not select a development operating point")
256
+ elif not args.dev:
257
+ parser.error("--dev is required unless --train-only is set")
258
+ validate_method(args.method, args.adapter, args.fresh_head)
259
+ if args.trainable_last_layers is not None and args.method != "full":
260
+ raise ValueError("A trainable encoder tail requires full checkpoint training")
261
+ if args.retention_targets is None:
262
+ if args.retention_weight is not None or args.retention_temperature is not None:
263
+ raise ValueError("Retention settings require explicit TRAIN targets")
264
+ else:
265
+ if args.method != "full" or args.fresh_head:
266
+ raise ValueError("Retention requires the unchanged complete initial head")
267
+ args.retention_weight = (
268
+ 0.5 if args.retention_weight is None else args.retention_weight
269
+ )
270
+ args.retention_temperature = (
271
+ 2.0 if args.retention_temperature is None else args.retention_temperature
272
+ )
273
+ if any(
274
+ not math.isfinite(value) or value <= 0
275
+ for value in (args.retention_weight, args.retention_temperature)
276
+ ):
277
+ raise ValueError(
278
+ "Retention weight and temperature must be finite and positive"
279
+ )
280
+ if args.training_order and (
281
+ args.balanced_sampling
282
+ or args.length_balanced_sampling
283
+ or args.source_balanced_sampling
284
+ or args.source_weights is not None
285
+ ):
286
+ raise ValueError("An explicit training order cannot also configure sampling")
287
+ if (
288
+ min(
289
+ args.steps,
290
+ args.batch_size,
291
+ args.accumulate,
292
+ args.max_length,
293
+ args.eval_every,
294
+ )
295
+ <= 0
296
+ or args.learning_rate <= 0
297
+ or (
298
+ args.microbatch_token_budget is not None
299
+ and args.microbatch_token_budget <= 0
300
+ )
301
+ ):
302
+ raise ValueError("Training budgets and learning rate must be positive")
303
+ if args.output.exists() and any(args.output.iterdir()):
304
+ raise ValueError("Refusing to overwrite a training run")
305
+ torch.set_num_threads(8)
306
+ torch.manual_seed(args.seed)
307
+ rng = random.Random(args.seed)
308
+ initial_artifacts = initial_artifact_receipt(args.base, args.adapter)
309
+ model, tokenizer, label_to_id, id_to_label = load_trainable_model(
310
+ args.base, args.contract, args.adapter, args.method, args.fresh_head
311
+ )
312
+ if model.config.problem_type != "single_label_classification":
313
+ raise ValueError("This training loop requires single-label targets")
314
+ if args.retention_targets is not None:
315
+ verify_full_checkpoint(model, args.base)
316
+ trainable_scope = (
317
+ configure_trainable_tail(model, args.trainable_last_layers)
318
+ if args.trainable_last_layers is not None
319
+ else None
320
+ )
321
+ frozen_parameters = (
322
+ frozen_parameter_receipt(model) if trainable_scope is not None else None
323
+ )
324
+ if not args.train_only:
325
+ validate_selection_options(
326
+ args.selection,
327
+ label_to_id,
328
+ args.positive_label,
329
+ args.selection_false_positive_budget,
330
+ )
331
+ if args.max_length > model.config.max_position_embeddings:
332
+ raise ValueError("Training budget exceeds checkpoint position capacity")
333
+ rows, dev = (
334
+ read_records(args.train, label_to_id),
335
+ [] if args.train_only else read_records(args.dev, label_to_id),
336
+ )
337
+ assert_disjoint(rows, dev)
338
+ if args.selection == BINARY_FP_SELECTION:
339
+ binary_selection_support(dev, label_to_id, args.positive_label)
340
+ train, rejected, pools = [], [], defaultdict(list)
341
+ for row in rows:
342
+ encoded = tokenizer(row["text"], truncation=False, padding=False)
343
+ if len(encoded["input_ids"]) > args.max_length:
344
+ rejected.append(
345
+ {
346
+ "id": row["id"],
347
+ "reason": "explicit context budget",
348
+ "tokens": len(encoded["input_ids"]),
349
+ }
350
+ )
351
+ continue
352
+ index = len(train)
353
+ train.append((row, encoded))
354
+ pools[row["label"]].append(index)
355
+ if not train:
356
+ raise ValueError("No training rows fit the explicit budget")
357
+ retention = None
358
+ if args.retention_targets is not None:
359
+ if any(
360
+ not isinstance(row.get("retention_replay", False), bool) for row, _ in train
361
+ ):
362
+ raise ValueError("retention_replay must be a boolean TRAIN annotation")
363
+ retention = RetentionCache.load(
364
+ args.retention_targets,
365
+ initialization=initial_artifacts,
366
+ contract_sha256=hashlib.sha256(
367
+ Path(args.contract).read_bytes()
368
+ ).hexdigest(),
369
+ tokenizer_files=tokenizer_file_receipt(args.base),
370
+ train_rows=[row for row, _ in train],
371
+ tokenized_rows={row["id"]: encoded for row, encoded in train},
372
+ label_to_id=label_to_id,
373
+ replay_ids=[
374
+ row["id"] for row, _ in train if row.get("retention_replay", False)
375
+ ],
376
+ )
377
+ train_lengths = [len(encoded["input_ids"]) for _, encoded in train]
378
+ if (
379
+ args.microbatch_token_budget is not None
380
+ and max(train_lengths) > args.microbatch_token_budget
381
+ ):
382
+ raise ValueError(
383
+ "Microbatch token budget cannot fit the longest eligible example"
384
+ )
385
+ training_order = (
386
+ load_training_order(
387
+ args.training_order,
388
+ [row for row, _ in train],
389
+ args.train,
390
+ steps=args.steps,
391
+ global_batch=args.batch_size * args.accumulate,
392
+ )
393
+ if args.training_order
394
+ else None
395
+ )
396
+ pool_values = list(pools.values())
397
+ length_pools = length_sampling_pools([row for row, _encoded in train])
398
+ balance_sources = args.source_balanced_sampling or args.source_weights is not None
399
+ source_weights = source_sampling_weights(
400
+ [row for row, _encoded in train], args.source_weights
401
+ )
402
+ source_pools = (
403
+ source_sampling_pools(
404
+ [row for row, _encoded in train], args.length_balanced_sampling
405
+ )
406
+ if balance_sources
407
+ else None
408
+ )
409
+ model.gradient_checkpointing_enable(
410
+ gradient_checkpointing_kwargs={"use_reentrant": False}
411
+ )
412
+ model.to("cuda")
413
+ model.train()
414
+ parameters = [
415
+ parameter for parameter in model.parameters() if parameter.requires_grad
416
+ ]
417
+ groups = optimizer_groups(model, args.learning_rate, args.head_learning_rate)
418
+ optimizer = torch.optim.AdamW(groups, weight_decay=0.01)
419
+ args.output.mkdir(parents=True, exist_ok=True)
420
+ metadata = {
421
+ "base_model": args.base_id,
422
+ "base_revision": args.base_revision,
423
+ "method": args.method,
424
+ "fresh_head": args.fresh_head,
425
+ "trainable_scope": trainable_scope,
426
+ "frozen_parameters": frozen_parameters,
427
+ "retention": (
428
+ {
429
+ "targets": retention.receipt,
430
+ "weight": args.retention_weight,
431
+ "temperature": args.retention_temperature,
432
+ "normalization": "all examples in the optimizer step",
433
+ }
434
+ if retention is not None
435
+ else None
436
+ ),
437
+ "initial_artifacts": initial_artifacts,
438
+ "initial_adapter_sha256": initial_artifacts.get("adapter_model.safetensors"),
439
+ "initial_adapter_config_sha256": initial_artifacts.get("adapter_config.json"),
440
+ "contract_sha256": hashlib.sha256(Path(args.contract).read_bytes()).hexdigest(),
441
+ "classifier_pooling": model.config.classifier_pooling,
442
+ "task_head": task_head_scope(model),
443
+ "problem_type": model.config.problem_type,
444
+ "train_files": file_receipts(args.train),
445
+ "dev_files": file_receipts(args.dev or []),
446
+ "train_only": args.train_only,
447
+ "checkpoint_policy": (
448
+ "final-only" if args.train_only else "development-and-final"
449
+ ),
450
+ "label2id": label_to_id,
451
+ "train_rows": len(train),
452
+ "dev_rows": len(dev),
453
+ "train_label_counts": label_counts([row for row, _ in train]),
454
+ "rejected": rejected,
455
+ "seed": args.seed,
456
+ "steps": args.steps,
457
+ "batch_size": args.batch_size,
458
+ "accumulate": args.accumulate,
459
+ "examples_per_optimizer_step": args.batch_size * args.accumulate,
460
+ "microbatch_token_budget": args.microbatch_token_budget,
461
+ "dropout_trajectory_equivalence": args.microbatch_token_budget is None,
462
+ "max_length": args.max_length,
463
+ "learning_rate": args.learning_rate,
464
+ "optimizer_groups": [
465
+ {
466
+ "name": group["name"],
467
+ "learning_rate": group["initial_lr"],
468
+ "parameters": sum(parameter.numel() for parameter in group["params"]),
469
+ }
470
+ for group in groups
471
+ ],
472
+ "balanced_sampling": args.balanced_sampling,
473
+ "training_order": training_order.receipt if training_order else None,
474
+ "length_balanced_sampling": args.length_balanced_sampling,
475
+ "source_balanced_sampling": balance_sources,
476
+ "source_sampling_probabilities": (
477
+ dict(
478
+ zip(
479
+ dict.fromkeys(row["source"] for row, _ in train),
480
+ source_weights,
481
+ strict=True,
482
+ )
483
+ )
484
+ if source_weights is not None
485
+ else None
486
+ ),
487
+ "implementation_sha256": hashlib.sha256(
488
+ Path(__file__).read_bytes()
489
+ ).hexdigest(),
490
+ "selection": None if args.train_only else args.selection,
491
+ "evaluation_dtype": None if args.train_only else args.evaluation_dtype,
492
+ "test_used": False,
493
+ "precision": "FP32 parameters and explicit FP32 mean CE / BF16 autocast",
494
+ "attention": "sdpa",
495
+ "checkpointing": "non-reentrant",
496
+ "parameters": sum(parameter.numel() for parameter in model.parameters()),
497
+ "trainable_parameters": sum(parameter.numel() for parameter in parameters),
498
+ }
499
+ if args.selection == BINARY_FP_SELECTION:
500
+ metadata["binary_selection"] = {
501
+ "positive_label": args.positive_label,
502
+ "false_positive_budget": args.selection_false_positive_budget,
503
+ "support": binary_selection_support(dev, label_to_id, args.positive_label),
504
+ "implementation_sha256": hashlib.sha256(
505
+ Path(__file__).with_name("selection.py").read_bytes()
506
+ ).hexdigest(),
507
+ }
508
+ (args.output / "run.json").write_text(json.dumps(metadata, indent=2) + "\n")
509
+ print(
510
+ json.dumps(
511
+ {
512
+ "event": "ready",
513
+ "train_rows": len(train),
514
+ "rejected_rows": len(rejected),
515
+ "trainable_parameters": metadata["trainable_parameters"],
516
+ }
517
+ ),
518
+ flush=True,
519
+ )
520
+ best = -1.0
521
+ best_key = None
522
+ drawn = Counter()
523
+
524
+ def evaluate_checkpoint(step):
525
+ nonlocal best, best_key
526
+ metrics, predictions = evaluate_records(
527
+ model,
528
+ tokenizer,
529
+ dev,
530
+ label_to_id,
531
+ id_to_label,
532
+ args.max_length,
533
+ dtype=args.evaluation_dtype,
534
+ )
535
+ operating_point = None
536
+ if args.selection == BINARY_FP_SELECTION:
537
+ operating_point = fit_binary_operating_point(
538
+ dev,
539
+ predictions,
540
+ label_to_id,
541
+ args.positive_label,
542
+ args.selection_false_positive_budget,
543
+ )
544
+ metrics["binary_fp_budget"] = operating_point
545
+ key = binary_checkpoint_key(operating_point)
546
+ score = operating_point["recall"] if key is not None else -1.0
547
+ (args.output / f"dev-predictions-step-{step}.jsonl").write_text(
548
+ "".join(json.dumps(row) + "\n" for row in predictions)
549
+ )
550
+ else:
551
+ score = selection_score(metrics, args.selection)
552
+ key = (score,)
553
+ (args.output / f"sampling-step-{step}.json").write_text(
554
+ json.dumps(sampling_exposure([row for row, _ in train], drawn), indent=2)
555
+ + "\n"
556
+ )
557
+ (args.output / f"dev-step-{step}.json").write_text(
558
+ json.dumps(metrics, indent=2) + "\n"
559
+ )
560
+ print(
561
+ json.dumps(
562
+ {
563
+ "event": "dev",
564
+ "step": step,
565
+ "score": score,
566
+ "accuracy": metrics["accuracy"],
567
+ "macro_f1": metrics["macro_f1"],
568
+ }
569
+ ),
570
+ flush=True,
571
+ )
572
+ if key is not None and (best_key is None or key > best_key):
573
+ best = score
574
+ best_key = key
575
+ if frozen_parameters is not None:
576
+ assert_frozen_parameters_preserved(frozen_parameters, model)
577
+ save_training_checkpoint(
578
+ model,
579
+ tokenizer,
580
+ args.output
581
+ / ("best-adapter" if args.method == "lora" else "best-model"),
582
+ args.method,
583
+ args.base_id,
584
+ args.base_revision,
585
+ )
586
+ (args.output / "selection.json").write_text(
587
+ json.dumps(
588
+ {
589
+ "step": step,
590
+ "score": score,
591
+ "metric": args.selection,
592
+ "test_used": False,
593
+ **(
594
+ {"operating_point": operating_point}
595
+ if operating_point is not None
596
+ else {}
597
+ ),
598
+ },
599
+ indent=2,
600
+ )
601
+ + "\n"
602
+ )
603
+
604
+ def draw_microbatch_indices():
605
+ return [
606
+ (
607
+ sample_source_balanced(
608
+ source_pools, rng, args.balanced_sampling, source_weights
609
+ )
610
+ if balance_sources
611
+ else (
612
+ sample_length_balanced(length_pools, rng, args.balanced_sampling)
613
+ if args.length_balanced_sampling
614
+ else (
615
+ rng.choice(rng.choice(pool_values))
616
+ if args.balanced_sampling
617
+ else rng.randrange(len(train))
618
+ )
619
+ )
620
+ )
621
+ for _ in range(args.batch_size)
622
+ ]
623
+
624
+ if not args.probe_only and not args.train_only:
625
+ evaluate_checkpoint(0)
626
+ for step in range(1, args.steps + 1):
627
+ optimizer.zero_grad(set_to_none=True)
628
+ torch.cuda.reset_peak_memory_stats()
629
+ torch.cuda.synchronize()
630
+ start, step_loss, lengths = time.perf_counter(), 0.0, []
631
+ step_ce_loss, step_retention_loss, retention_examples = 0.0, 0.0, 0
632
+ if training_order:
633
+ planned = training_order.indices_for_step(step)
634
+ microbatches = (
635
+ token_budget_microbatches(
636
+ planned, train_lengths, args.microbatch_token_budget
637
+ )
638
+ if args.microbatch_token_budget is not None
639
+ else [
640
+ planned[start : start + args.batch_size]
641
+ for start in range(0, len(planned), args.batch_size)
642
+ ]
643
+ )
644
+ elif args.microbatch_token_budget is None:
645
+ microbatches = (draw_microbatch_indices() for _ in range(args.accumulate))
646
+ else:
647
+ sampled = [
648
+ index
649
+ for _ in range(args.accumulate)
650
+ for index in draw_microbatch_indices()
651
+ ]
652
+ microbatches = token_budget_microbatches(
653
+ sampled, train_lengths, args.microbatch_token_budget
654
+ )
655
+ microbatch_count, max_padded_tokens = 0, 0
656
+ for indices in microbatches:
657
+ microbatch_count += 1
658
+ max_padded_tokens = max(
659
+ max_padded_tokens, max(train_lengths[i] for i in indices) * len(indices)
660
+ )
661
+ drawn.update(indices)
662
+ selected = [train[index] for index in indices]
663
+ lengths.extend(len(encoded["input_ids"]) for _, encoded in selected)
664
+ batch = tokenizer.pad(
665
+ [encoded for _, encoded in selected], padding=True, return_tensors="pt"
666
+ )
667
+ batch = {key: value.to("cuda") for key, value in batch.items()}
668
+ gold = torch.tensor(
669
+ [label_to_id[row["label"]] for row, _ in selected], device="cuda"
670
+ )
671
+ with torch.autocast("cuda", dtype=torch.bfloat16):
672
+ logits = model(**batch).logits
673
+ loss = (
674
+ microbatch_loss(logits, gold, args.accumulate)
675
+ if args.microbatch_token_budget is None
676
+ else global_batch_loss(
677
+ logits, gold, args.batch_size * args.accumulate
678
+ )
679
+ )
680
+ step_ce_loss += float(loss.detach())
681
+ if retention is not None:
682
+ teacher_logits, eligible = retention.targets(
683
+ [row for row, _ in selected], device=logits.device
684
+ )
685
+ preservation = args.retention_weight * masked_teacher_kl(
686
+ logits,
687
+ teacher_logits,
688
+ eligible,
689
+ examples_per_step=args.batch_size * args.accumulate,
690
+ temperature=args.retention_temperature,
691
+ )
692
+ loss = loss + preservation
693
+ step_retention_loss += float(preservation.detach())
694
+ retention_examples += int(eligible.sum())
695
+ if not bool(torch.isfinite(loss)):
696
+ raise ValueError(f"Non-finite loss at step {step}")
697
+ loss.backward()
698
+ step_loss += float(loss.detach())
699
+ del logits, loss, batch
700
+ gradients = [
701
+ parameter.grad for parameter in parameters if parameter.grad is not None
702
+ ]
703
+ if not gradients or not all(
704
+ bool(torch.isfinite(gradient).all()) for gradient in gradients
705
+ ):
706
+ raise ValueError(f"Missing or non-finite gradients at step {step}")
707
+ norm = torch.nn.utils.clip_grad_norm_(parameters, 1.0)
708
+ warmup = max(1, round(args.steps * 0.06))
709
+ rate = (
710
+ step / warmup
711
+ if step <= warmup
712
+ else 0.5
713
+ * (1 + math.cos(math.pi * (step - warmup) / max(1, args.steps - warmup)))
714
+ )
715
+ for group in optimizer.param_groups:
716
+ group["lr"] = group["initial_lr"] * rate
717
+ optimizer.step()
718
+ if training_order:
719
+ trace = training_order.record_step(step, microbatches)
720
+ with (args.output / "actual-training-order.jsonl").open("a") as stream:
721
+ stream.write(json.dumps(trace) + "\n")
722
+ torch.cuda.synchronize()
723
+ log = {
724
+ "step": step,
725
+ "loss": step_loss,
726
+ "cross_entropy_loss": step_ce_loss,
727
+ "retention_loss": step_retention_loss,
728
+ "retention_examples": retention_examples,
729
+ "grad_norm": float(norm),
730
+ "finite": True,
731
+ "gradient_tensors": len(gradients),
732
+ "max_tokens": max(lengths),
733
+ "mean_tokens": sum(lengths) / len(lengths),
734
+ "microbatches": microbatch_count,
735
+ "max_padded_tokens": max_padded_tokens,
736
+ "step_seconds": time.perf_counter() - start,
737
+ "peak_allocated_gib": torch.cuda.max_memory_allocated() / 2**30,
738
+ "peak_reserved_gib": torch.cuda.max_memory_reserved() / 2**30,
739
+ }
740
+ with (args.output / "steps.jsonl").open("a") as stream:
741
+ stream.write(json.dumps(log) + "\n")
742
+ if step == 1 or step % 10 == 0 or args.probe_only:
743
+ print(json.dumps(log), flush=True)
744
+ if (
745
+ not args.probe_only
746
+ and not args.train_only
747
+ and (step % args.eval_every == 0 or step == args.steps)
748
+ ):
749
+ evaluate_checkpoint(step)
750
+ if training_order:
751
+ order_receipt = training_order.finish()
752
+ order_receipt["actual_trace_sha256"] = hashlib.sha256(
753
+ (args.output / "actual-training-order.jsonl").read_bytes()
754
+ ).hexdigest()
755
+ (args.output / "training-order-completed.json").write_text(
756
+ json.dumps(order_receipt, indent=2) + "\n"
757
+ )
758
+ (args.output / "sampling-final.json").write_text(
759
+ json.dumps(sampling_exposure([row for row, _ in train], drawn), indent=2) + "\n"
760
+ )
761
+ if frozen_parameters is not None:
762
+ assert_frozen_parameters_preserved(frozen_parameters, model)
763
+ if not args.probe_only:
764
+ save_training_checkpoint(
765
+ model,
766
+ tokenizer,
767
+ args.output / ("last-adapter" if args.method == "lora" else "last-model"),
768
+ args.method,
769
+ args.base_id,
770
+ args.base_revision,
771
+ )
772
+ if best_key is None and not args.train_only:
773
+ raise ValueError(
774
+ "No checkpoint met the development FP budget; last checkpoint and "
775
+ "infeasibility receipts were retained without selecting a best checkpoint"
776
+ )
777
+ print(
778
+ json.dumps(
779
+ {
780
+ "event": "complete",
781
+ "best_dev_score": (
782
+ best if not (args.probe_only or args.train_only) else None
783
+ ),
784
+ }
785
+ ),
786
+ flush=True,
787
+ )
788
+
789
+
790
+ if __name__ == "__main__":
791
+ main()
reproducibility/sequence_repair/training_order.py ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Replay a complete, content-bound training order without changing optimization."""
2
+
3
+ import hashlib
4
+ import json
5
+ from collections import Counter
6
+ from dataclasses import dataclass, field
7
+ from pathlib import Path
8
+
9
+ from .data import file_receipts
10
+
11
+
12
+ def ordered_ids_sha256(ids):
13
+ """Canonical identity preserves UTF-8 IDs, duplicates, and their full order."""
14
+ return hashlib.sha256(
15
+ json.dumps(ids, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
16
+ ).hexdigest()
17
+
18
+
19
+ def _unique_object(pairs):
20
+ result = {}
21
+ for key, value in pairs:
22
+ if key in result:
23
+ raise ValueError("Duplicate training-order field")
24
+ result[key] = value
25
+ return result
26
+
27
+
28
+ @dataclass
29
+ class TrainingOrder:
30
+ """A step can only complete once, with every planned example accounted for."""
31
+
32
+ indices: tuple[int, ...]
33
+ eligible_ids: tuple[str, ...]
34
+ steps: int
35
+ global_batch: int
36
+ receipt: dict
37
+ completed_steps: int = field(default=0, init=False)
38
+
39
+ def indices_for_step(self, step):
40
+ if (
41
+ type(step) is not int
42
+ or step != self.completed_steps + 1
43
+ or step > self.steps
44
+ ):
45
+ raise ValueError("Training order requires the next unconsumed step")
46
+ start = (step - 1) * self.global_batch
47
+ return list(self.indices[start : start + self.global_batch])
48
+
49
+ def record_step(self, step, microbatches):
50
+ planned = self.indices_for_step(step)
51
+ actual = [index for batch in microbatches for index in batch]
52
+ if Counter(planned) != Counter(actual):
53
+ raise ValueError("Microbatches dropped or duplicated planned examples")
54
+ self.completed_steps = step
55
+ return {
56
+ "step": step,
57
+ "planned_ids": [self.eligible_ids[index] for index in planned],
58
+ "microbatch_ids": [
59
+ [self.eligible_ids[index] for index in batch] for batch in microbatches
60
+ ],
61
+ }
62
+
63
+ def finish(self):
64
+ if self.completed_steps != self.steps:
65
+ raise ValueError("Training ended before consuming the complete order")
66
+ return {
67
+ **self.receipt,
68
+ "completed_steps": self.completed_steps,
69
+ "completed_draws": self.completed_steps * self.global_batch,
70
+ }
71
+
72
+
73
+ def load_training_order(path, rows, train_paths, *, steps, global_batch):
74
+ """Validate exact source bytes, eligible IDs, budgets, and planned draws."""
75
+ raw = Path(path).read_bytes()
76
+ value = json.loads(raw, object_pairs_hook=_unique_object)
77
+ required = {
78
+ "version",
79
+ "train_files",
80
+ "eligible_ids_sha256",
81
+ "steps",
82
+ "global_batch",
83
+ "ids",
84
+ "ids_sha256",
85
+ }
86
+ if not isinstance(value, dict) or set(value) != required:
87
+ raise ValueError("Training order has unknown or missing fields")
88
+ if type(value["version"]) is not int or value["version"] != 1:
89
+ raise ValueError("Unsupported training-order version")
90
+ for name, expected in (("steps", steps), ("global_batch", global_batch)):
91
+ if (
92
+ type(expected) is not int
93
+ or expected <= 0
94
+ or type(value[name]) is not int
95
+ or value[name] != expected
96
+ ):
97
+ raise ValueError("Training order does not match the optimizer budget")
98
+ if value["train_files"] != file_receipts(train_paths):
99
+ raise ValueError("Training order does not match TRAIN file contents")
100
+ eligible = [row.get("id") for row in rows]
101
+ if (
102
+ not eligible
103
+ or any(not isinstance(key, str) or not key for key in eligible)
104
+ or len(set(eligible)) != len(eligible)
105
+ ):
106
+ raise ValueError("Eligible TRAIN IDs must be nonempty unique strings")
107
+ if value["eligible_ids_sha256"] != ordered_ids_sha256(eligible):
108
+ raise ValueError("Training order does not match eligible TRAIN IDs")
109
+ ids = value["ids"]
110
+ if (
111
+ not isinstance(ids, list)
112
+ or len(ids) != steps * global_batch
113
+ or any(not isinstance(key, str) or not key for key in ids)
114
+ ):
115
+ raise ValueError("Training order needs exactly steps times global-batch IDs")
116
+ if value["ids_sha256"] != ordered_ids_sha256(ids):
117
+ raise ValueError("Training-order ID sequence hash mismatch")
118
+ positions = {key: index for index, key in enumerate(eligible)}
119
+ if any(key not in positions for key in ids):
120
+ raise ValueError("Training order references unknown or filtered TRAIN IDs")
121
+ receipt = {
122
+ "file": Path(path).name,
123
+ "sha256": hashlib.sha256(raw).hexdigest(),
124
+ **{key: value[key] for key in sorted(required - {"ids"})},
125
+ "draws": len(ids),
126
+ "unique_rows": len(set(ids)),
127
+ }
128
+ return TrainingOrder(
129
+ tuple(positions[key] for key in ids),
130
+ tuple(eligible),
131
+ steps,
132
+ global_batch,
133
+ receipt,
134
+ )
reproducibility/source-files.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "runtime_mapping.py": "ec0c553ef625001c09c53a27659cc5162629e303bdef6a93c6091767f49b7fd9",
3
+ "retention.py": "3c8bc8aa60388508bc400bd68a6de2ed6ac18b48a24d3acd66d8e591223736eb",
4
+ "complete_adapter.py": "bc9a9365b1740b18d9cbe59d5250d8b5d214a0e133850c4921857c8e870ea13a",
5
+ "hydrate_annotations.py": "ea0c44b51afddbeb7dabf084db006eed545022ed819fa40682f4be846b207d62",
6
+ "export.py": "2f3ec1b038eae154ad32b28dc3cdc465e57e8d8b6fa842c2b67b0a66d3938fa9",
7
+ "task_head.py": "9d2a19743980c35e224c752a35e1e91a7e82dee3430f83b9008a4d01270a5a42",
8
+ "prepare_context.py": "9f9fe1ebd52e97d3e6901dc42bbf81f7e0703b7f2293703eabd89acd900a254a",
9
+ "corpus.py": "ba3d4f91607db085e55ae1c76701b9b1319fea5ee7dece9851d4ee7375bca108",
10
+ "prepare_retention.py": "56f48cc26b4889f93b43734e66ad8aaccd0046701519278b6b446d495f605ff4",
11
+ "initialize.py": "ade17c61ba94231a83e124e368bda4f468574cbb77689b6f71694c6489bb94a6",
12
+ "evaluate.py": "41617631838a1f72e33d402470a9bebd1ab0d7b42b80ac7e8c424a487068c562",
13
+ "selection.py": "764150454d150ad8971956faa2198d84380c8ee28f23ea63a650b07d045fc735",
14
+ "data.py": "84f922c20a2f3b029f7d363cf7f96f98cd239669f7777de099c63cfa1a1ef199",
15
+ "model.py": "c85ca12237e8735e20289e6e630803c59a6919dd473021d42291956c4b32f434",
16
+ "optimization.py": "71dde43dbe288cd8fab636a9266905f7898bac66ae155ded5158e37786492082",
17
+ "__init__.py": "04cd2a4c037dd1cc2070d57bea979cacb76d4660313a190fdf612007cb33b88e",
18
+ "context.py": "8e51031478aca4a825a77d057b2a66a9d09d80e42fb66ccbec3c527f3a891303",
19
+ "fetch_sources.py": "1abd30d33f456357553facdbde11116134835a087009c63c433faeccf8214638",
20
+ "train.py": "8b91020b3fcf93ae5925e9d15597d6f37ea3ca5d5c03745a2096b76be246e038",
21
+ "training_order.py": "88492bbb97b93743d637474595cffbc82e79d18bd115245adf60ffadea3bef44"
22
+ }
reproducibility/training.json ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "steps": 100,
3
+ "gpu": "NVIDIA L4",
4
+ "torch": "2.8.0+cu128",
5
+ "cuda": "12.8",
6
+ "python": "3.12.10",
7
+ "selection": {
8
+ "step": 75,
9
+ "score": 1.0,
10
+ "metric": "macro-f1",
11
+ "test_used": false
12
+ },
13
+ "base_model": "vllm-sr/Vela-1.0-Encoder-307M",
14
+ "base_revision": "720ab37904ce15054068d429381bfae7549f00f6",
15
+ "method": "full; fresh head; final two encoder layers",
16
+ "batch_size": 2,
17
+ "accumulation": 2,
18
+ "max_length": 256,
19
+ "seed": 42,
20
+ "learning_rate": 2e-05,
21
+ "head_learning_rate": 0.0001,
22
+ "dataset_files": {
23
+ "train.jsonl": "69022836ee506922a74b3583088a242fcbedc5fd181c4dd7bcea4e7eca60f814",
24
+ "dev.jsonl": "5909fe10961b0c6500a5e7d6ac9efd99b114c16f3545d45cd04036a070dbd1c3"
25
+ }
26
+ }
special_tokens_map.json ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "additional_special_tokens": [
3
+ "<start_of_turn>",
4
+ "<end_of_turn>"
5
+ ],
6
+ "bos_token": {
7
+ "content": "<bos>",
8
+ "lstrip": false,
9
+ "normalized": false,
10
+ "rstrip": false,
11
+ "single_word": false
12
+ },
13
+ "cls_token": {
14
+ "content": "<bos>",
15
+ "lstrip": false,
16
+ "normalized": false,
17
+ "rstrip": false,
18
+ "single_word": false
19
+ },
20
+ "eos_token": {
21
+ "content": "<eos>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false
26
+ },
27
+ "mask_token": {
28
+ "content": "<mask>",
29
+ "lstrip": true,
30
+ "normalized": false,
31
+ "rstrip": false,
32
+ "single_word": false
33
+ },
34
+ "pad_token": {
35
+ "content": "<pad>",
36
+ "lstrip": false,
37
+ "normalized": false,
38
+ "rstrip": false,
39
+ "single_word": false
40
+ },
41
+ "sep_token": {
42
+ "content": "<eos>",
43
+ "lstrip": false,
44
+ "normalized": false,
45
+ "rstrip": false,
46
+ "single_word": false
47
+ },
48
+ "unk_token": {
49
+ "content": "<unk>",
50
+ "lstrip": false,
51
+ "normalized": false,
52
+ "rstrip": false,
53
+ "single_word": false
54
+ }
55
+ }
task-head.json ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "architecture": "ModernBertForSequenceClassification",
3
+ "modules_to_save": null,
4
+ "trainable_task_head_parameters": 592130,
5
+ "tensors": {
6
+ "head.dense.weight": {
7
+ "shape": [
8
+ 768,
9
+ 768
10
+ ],
11
+ "dtype": "torch.float32",
12
+ "numel": 589824,
13
+ "trainable": true,
14
+ "adapter_owned": false,
15
+ "sha256": "01a8bfc3e8af91a9901f5088ec0fb1cdb1f1b8c47526a5d5e2fe5a94d7383003"
16
+ },
17
+ "head.norm.weight": {
18
+ "shape": [
19
+ 768
20
+ ],
21
+ "dtype": "torch.float32",
22
+ "numel": 768,
23
+ "trainable": true,
24
+ "adapter_owned": false,
25
+ "sha256": "fd989126d90b0d85ba4e8863c791567d526c8d6bed155fc05672489c906a91b5"
26
+ },
27
+ "classifier.weight": {
28
+ "shape": [
29
+ 2,
30
+ 768
31
+ ],
32
+ "dtype": "torch.float32",
33
+ "numel": 1536,
34
+ "trainable": true,
35
+ "adapter_owned": false,
36
+ "sha256": "84ca652ca12370d26a59fc4039312c112dd5079c834a04f43cc48c568b0873ee"
37
+ },
38
+ "classifier.bias": {
39
+ "shape": [
40
+ 2
41
+ ],
42
+ "dtype": "torch.float32",
43
+ "numel": 2,
44
+ "trainable": true,
45
+ "adapter_owned": false,
46
+ "sha256": "eace58362ea99f963002fb0ba0a36ab6d2ddfc1a504668108853dd5dbf1cba92"
47
+ }
48
+ }
49
+ }
tokenizer.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:609d8f4c067cd3950f88594c5a802616cea245823836ef5848ee4fc40aab5b6f
3
+ size 34363188
tokenizer_config.json ADDED
@@ -0,0 +1,2018 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "add_bos_token": true,
3
+ "added_tokens_decoder": {
4
+ "0": {
5
+ "content": "<pad>",
6
+ "lstrip": false,
7
+ "normalized": false,
8
+ "rstrip": false,
9
+ "single_word": false,
10
+ "special": true
11
+ },
12
+ "1": {
13
+ "content": "<eos>",
14
+ "lstrip": false,
15
+ "normalized": false,
16
+ "rstrip": false,
17
+ "single_word": false,
18
+ "special": true
19
+ },
20
+ "2": {
21
+ "content": "<bos>",
22
+ "lstrip": false,
23
+ "normalized": false,
24
+ "rstrip": false,
25
+ "single_word": false,
26
+ "special": true
27
+ },
28
+ "3": {
29
+ "content": "<unk>",
30
+ "lstrip": false,
31
+ "normalized": false,
32
+ "rstrip": false,
33
+ "single_word": false,
34
+ "special": true
35
+ },
36
+ "4": {
37
+ "content": "<mask>",
38
+ "lstrip": true,
39
+ "normalized": false,
40
+ "rstrip": false,
41
+ "single_word": false,
42
+ "special": true
43
+ },
44
+ "5": {
45
+ "content": "<2mass>",
46
+ "lstrip": false,
47
+ "normalized": false,
48
+ "rstrip": false,
49
+ "single_word": false,
50
+ "special": false
51
+ },
52
+ "6": {
53
+ "content": "[@BOS@]",
54
+ "lstrip": false,
55
+ "normalized": false,
56
+ "rstrip": false,
57
+ "single_word": false,
58
+ "special": false
59
+ },
60
+ "7": {
61
+ "content": "<unused0>",
62
+ "lstrip": false,
63
+ "normalized": false,
64
+ "rstrip": false,
65
+ "single_word": false,
66
+ "special": false
67
+ },
68
+ "8": {
69
+ "content": "<unused1>",
70
+ "lstrip": false,
71
+ "normalized": false,
72
+ "rstrip": false,
73
+ "single_word": false,
74
+ "special": false
75
+ },
76
+ "9": {
77
+ "content": "<unused2>",
78
+ "lstrip": false,
79
+ "normalized": false,
80
+ "rstrip": false,
81
+ "single_word": false,
82
+ "special": false
83
+ },
84
+ "10": {
85
+ "content": "<unused3>",
86
+ "lstrip": false,
87
+ "normalized": false,
88
+ "rstrip": false,
89
+ "single_word": false,
90
+ "special": false
91
+ },
92
+ "11": {
93
+ "content": "<unused4>",
94
+ "lstrip": false,
95
+ "normalized": false,
96
+ "rstrip": false,
97
+ "single_word": false,
98
+ "special": false
99
+ },
100
+ "12": {
101
+ "content": "<unused5>",
102
+ "lstrip": false,
103
+ "normalized": false,
104
+ "rstrip": false,
105
+ "single_word": false,
106
+ "special": false
107
+ },
108
+ "13": {
109
+ "content": "<unused6>",
110
+ "lstrip": false,
111
+ "normalized": false,
112
+ "rstrip": false,
113
+ "single_word": false,
114
+ "special": false
115
+ },
116
+ "14": {
117
+ "content": "<unused7>",
118
+ "lstrip": false,
119
+ "normalized": false,
120
+ "rstrip": false,
121
+ "single_word": false,
122
+ "special": false
123
+ },
124
+ "15": {
125
+ "content": "<unused8>",
126
+ "lstrip": false,
127
+ "normalized": false,
128
+ "rstrip": false,
129
+ "single_word": false,
130
+ "special": false
131
+ },
132
+ "16": {
133
+ "content": "<unused9>",
134
+ "lstrip": false,
135
+ "normalized": false,
136
+ "rstrip": false,
137
+ "single_word": false,
138
+ "special": false
139
+ },
140
+ "17": {
141
+ "content": "<unused10>",
142
+ "lstrip": false,
143
+ "normalized": false,
144
+ "rstrip": false,
145
+ "single_word": false,
146
+ "special": false
147
+ },
148
+ "18": {
149
+ "content": "<unused11>",
150
+ "lstrip": false,
151
+ "normalized": false,
152
+ "rstrip": false,
153
+ "single_word": false,
154
+ "special": false
155
+ },
156
+ "19": {
157
+ "content": "<unused12>",
158
+ "lstrip": false,
159
+ "normalized": false,
160
+ "rstrip": false,
161
+ "single_word": false,
162
+ "special": false
163
+ },
164
+ "20": {
165
+ "content": "<unused13>",
166
+ "lstrip": false,
167
+ "normalized": false,
168
+ "rstrip": false,
169
+ "single_word": false,
170
+ "special": false
171
+ },
172
+ "21": {
173
+ "content": "<unused14>",
174
+ "lstrip": false,
175
+ "normalized": false,
176
+ "rstrip": false,
177
+ "single_word": false,
178
+ "special": false
179
+ },
180
+ "22": {
181
+ "content": "<unused15>",
182
+ "lstrip": false,
183
+ "normalized": false,
184
+ "rstrip": false,
185
+ "single_word": false,
186
+ "special": false
187
+ },
188
+ "23": {
189
+ "content": "<unused16>",
190
+ "lstrip": false,
191
+ "normalized": false,
192
+ "rstrip": false,
193
+ "single_word": false,
194
+ "special": false
195
+ },
196
+ "24": {
197
+ "content": "<unused17>",
198
+ "lstrip": false,
199
+ "normalized": false,
200
+ "rstrip": false,
201
+ "single_word": false,
202
+ "special": false
203
+ },
204
+ "25": {
205
+ "content": "<unused18>",
206
+ "lstrip": false,
207
+ "normalized": false,
208
+ "rstrip": false,
209
+ "single_word": false,
210
+ "special": false
211
+ },
212
+ "26": {
213
+ "content": "<unused19>",
214
+ "lstrip": false,
215
+ "normalized": false,
216
+ "rstrip": false,
217
+ "single_word": false,
218
+ "special": false
219
+ },
220
+ "27": {
221
+ "content": "<unused20>",
222
+ "lstrip": false,
223
+ "normalized": false,
224
+ "rstrip": false,
225
+ "single_word": false,
226
+ "special": false
227
+ },
228
+ "28": {
229
+ "content": "<unused21>",
230
+ "lstrip": false,
231
+ "normalized": false,
232
+ "rstrip": false,
233
+ "single_word": false,
234
+ "special": false
235
+ },
236
+ "29": {
237
+ "content": "<unused22>",
238
+ "lstrip": false,
239
+ "normalized": false,
240
+ "rstrip": false,
241
+ "single_word": false,
242
+ "special": false
243
+ },
244
+ "30": {
245
+ "content": "<unused23>",
246
+ "lstrip": false,
247
+ "normalized": false,
248
+ "rstrip": false,
249
+ "single_word": false,
250
+ "special": false
251
+ },
252
+ "31": {
253
+ "content": "<unused24>",
254
+ "lstrip": false,
255
+ "normalized": false,
256
+ "rstrip": false,
257
+ "single_word": false,
258
+ "special": false
259
+ },
260
+ "32": {
261
+ "content": "<unused25>",
262
+ "lstrip": false,
263
+ "normalized": false,
264
+ "rstrip": false,
265
+ "single_word": false,
266
+ "special": false
267
+ },
268
+ "33": {
269
+ "content": "<unused26>",
270
+ "lstrip": false,
271
+ "normalized": false,
272
+ "rstrip": false,
273
+ "single_word": false,
274
+ "special": false
275
+ },
276
+ "34": {
277
+ "content": "<unused27>",
278
+ "lstrip": false,
279
+ "normalized": false,
280
+ "rstrip": false,
281
+ "single_word": false,
282
+ "special": false
283
+ },
284
+ "35": {
285
+ "content": "<unused28>",
286
+ "lstrip": false,
287
+ "normalized": false,
288
+ "rstrip": false,
289
+ "single_word": false,
290
+ "special": false
291
+ },
292
+ "36": {
293
+ "content": "<unused29>",
294
+ "lstrip": false,
295
+ "normalized": false,
296
+ "rstrip": false,
297
+ "single_word": false,
298
+ "special": false
299
+ },
300
+ "37": {
301
+ "content": "<unused30>",
302
+ "lstrip": false,
303
+ "normalized": false,
304
+ "rstrip": false,
305
+ "single_word": false,
306
+ "special": false
307
+ },
308
+ "38": {
309
+ "content": "<unused31>",
310
+ "lstrip": false,
311
+ "normalized": false,
312
+ "rstrip": false,
313
+ "single_word": false,
314
+ "special": false
315
+ },
316
+ "39": {
317
+ "content": "<unused32>",
318
+ "lstrip": false,
319
+ "normalized": false,
320
+ "rstrip": false,
321
+ "single_word": false,
322
+ "special": false
323
+ },
324
+ "40": {
325
+ "content": "<unused33>",
326
+ "lstrip": false,
327
+ "normalized": false,
328
+ "rstrip": false,
329
+ "single_word": false,
330
+ "special": false
331
+ },
332
+ "41": {
333
+ "content": "<unused34>",
334
+ "lstrip": false,
335
+ "normalized": false,
336
+ "rstrip": false,
337
+ "single_word": false,
338
+ "special": false
339
+ },
340
+ "42": {
341
+ "content": "<unused35>",
342
+ "lstrip": false,
343
+ "normalized": false,
344
+ "rstrip": false,
345
+ "single_word": false,
346
+ "special": false
347
+ },
348
+ "43": {
349
+ "content": "<unused36>",
350
+ "lstrip": false,
351
+ "normalized": false,
352
+ "rstrip": false,
353
+ "single_word": false,
354
+ "special": false
355
+ },
356
+ "44": {
357
+ "content": "<unused37>",
358
+ "lstrip": false,
359
+ "normalized": false,
360
+ "rstrip": false,
361
+ "single_word": false,
362
+ "special": false
363
+ },
364
+ "45": {
365
+ "content": "<unused38>",
366
+ "lstrip": false,
367
+ "normalized": false,
368
+ "rstrip": false,
369
+ "single_word": false,
370
+ "special": false
371
+ },
372
+ "46": {
373
+ "content": "<unused39>",
374
+ "lstrip": false,
375
+ "normalized": false,
376
+ "rstrip": false,
377
+ "single_word": false,
378
+ "special": false
379
+ },
380
+ "47": {
381
+ "content": "<unused40>",
382
+ "lstrip": false,
383
+ "normalized": false,
384
+ "rstrip": false,
385
+ "single_word": false,
386
+ "special": false
387
+ },
388
+ "48": {
389
+ "content": "<unused41>",
390
+ "lstrip": false,
391
+ "normalized": false,
392
+ "rstrip": false,
393
+ "single_word": false,
394
+ "special": false
395
+ },
396
+ "49": {
397
+ "content": "<unused42>",
398
+ "lstrip": false,
399
+ "normalized": false,
400
+ "rstrip": false,
401
+ "single_word": false,
402
+ "special": false
403
+ },
404
+ "50": {
405
+ "content": "<unused43>",
406
+ "lstrip": false,
407
+ "normalized": false,
408
+ "rstrip": false,
409
+ "single_word": false,
410
+ "special": false
411
+ },
412
+ "51": {
413
+ "content": "<unused44>",
414
+ "lstrip": false,
415
+ "normalized": false,
416
+ "rstrip": false,
417
+ "single_word": false,
418
+ "special": false
419
+ },
420
+ "52": {
421
+ "content": "<unused45>",
422
+ "lstrip": false,
423
+ "normalized": false,
424
+ "rstrip": false,
425
+ "single_word": false,
426
+ "special": false
427
+ },
428
+ "53": {
429
+ "content": "<unused46>",
430
+ "lstrip": false,
431
+ "normalized": false,
432
+ "rstrip": false,
433
+ "single_word": false,
434
+ "special": false
435
+ },
436
+ "54": {
437
+ "content": "<unused47>",
438
+ "lstrip": false,
439
+ "normalized": false,
440
+ "rstrip": false,
441
+ "single_word": false,
442
+ "special": false
443
+ },
444
+ "55": {
445
+ "content": "<unused48>",
446
+ "lstrip": false,
447
+ "normalized": false,
448
+ "rstrip": false,
449
+ "single_word": false,
450
+ "special": false
451
+ },
452
+ "56": {
453
+ "content": "<unused49>",
454
+ "lstrip": false,
455
+ "normalized": false,
456
+ "rstrip": false,
457
+ "single_word": false,
458
+ "special": false
459
+ },
460
+ "57": {
461
+ "content": "<unused50>",
462
+ "lstrip": false,
463
+ "normalized": false,
464
+ "rstrip": false,
465
+ "single_word": false,
466
+ "special": false
467
+ },
468
+ "58": {
469
+ "content": "<unused51>",
470
+ "lstrip": false,
471
+ "normalized": false,
472
+ "rstrip": false,
473
+ "single_word": false,
474
+ "special": false
475
+ },
476
+ "59": {
477
+ "content": "<unused52>",
478
+ "lstrip": false,
479
+ "normalized": false,
480
+ "rstrip": false,
481
+ "single_word": false,
482
+ "special": false
483
+ },
484
+ "60": {
485
+ "content": "<unused53>",
486
+ "lstrip": false,
487
+ "normalized": false,
488
+ "rstrip": false,
489
+ "single_word": false,
490
+ "special": false
491
+ },
492
+ "61": {
493
+ "content": "<unused54>",
494
+ "lstrip": false,
495
+ "normalized": false,
496
+ "rstrip": false,
497
+ "single_word": false,
498
+ "special": false
499
+ },
500
+ "62": {
501
+ "content": "<unused55>",
502
+ "lstrip": false,
503
+ "normalized": false,
504
+ "rstrip": false,
505
+ "single_word": false,
506
+ "special": false
507
+ },
508
+ "63": {
509
+ "content": "<unused56>",
510
+ "lstrip": false,
511
+ "normalized": false,
512
+ "rstrip": false,
513
+ "single_word": false,
514
+ "special": false
515
+ },
516
+ "64": {
517
+ "content": "<unused57>",
518
+ "lstrip": false,
519
+ "normalized": false,
520
+ "rstrip": false,
521
+ "single_word": false,
522
+ "special": false
523
+ },
524
+ "65": {
525
+ "content": "<unused58>",
526
+ "lstrip": false,
527
+ "normalized": false,
528
+ "rstrip": false,
529
+ "single_word": false,
530
+ "special": false
531
+ },
532
+ "66": {
533
+ "content": "<unused59>",
534
+ "lstrip": false,
535
+ "normalized": false,
536
+ "rstrip": false,
537
+ "single_word": false,
538
+ "special": false
539
+ },
540
+ "67": {
541
+ "content": "<unused60>",
542
+ "lstrip": false,
543
+ "normalized": false,
544
+ "rstrip": false,
545
+ "single_word": false,
546
+ "special": false
547
+ },
548
+ "68": {
549
+ "content": "<unused61>",
550
+ "lstrip": false,
551
+ "normalized": false,
552
+ "rstrip": false,
553
+ "single_word": false,
554
+ "special": false
555
+ },
556
+ "69": {
557
+ "content": "<unused62>",
558
+ "lstrip": false,
559
+ "normalized": false,
560
+ "rstrip": false,
561
+ "single_word": false,
562
+ "special": false
563
+ },
564
+ "70": {
565
+ "content": "<unused63>",
566
+ "lstrip": false,
567
+ "normalized": false,
568
+ "rstrip": false,
569
+ "single_word": false,
570
+ "special": false
571
+ },
572
+ "71": {
573
+ "content": "<unused64>",
574
+ "lstrip": false,
575
+ "normalized": false,
576
+ "rstrip": false,
577
+ "single_word": false,
578
+ "special": false
579
+ },
580
+ "72": {
581
+ "content": "<unused65>",
582
+ "lstrip": false,
583
+ "normalized": false,
584
+ "rstrip": false,
585
+ "single_word": false,
586
+ "special": false
587
+ },
588
+ "73": {
589
+ "content": "<unused66>",
590
+ "lstrip": false,
591
+ "normalized": false,
592
+ "rstrip": false,
593
+ "single_word": false,
594
+ "special": false
595
+ },
596
+ "74": {
597
+ "content": "<unused67>",
598
+ "lstrip": false,
599
+ "normalized": false,
600
+ "rstrip": false,
601
+ "single_word": false,
602
+ "special": false
603
+ },
604
+ "75": {
605
+ "content": "<unused68>",
606
+ "lstrip": false,
607
+ "normalized": false,
608
+ "rstrip": false,
609
+ "single_word": false,
610
+ "special": false
611
+ },
612
+ "76": {
613
+ "content": "<unused69>",
614
+ "lstrip": false,
615
+ "normalized": false,
616
+ "rstrip": false,
617
+ "single_word": false,
618
+ "special": false
619
+ },
620
+ "77": {
621
+ "content": "<unused70>",
622
+ "lstrip": false,
623
+ "normalized": false,
624
+ "rstrip": false,
625
+ "single_word": false,
626
+ "special": false
627
+ },
628
+ "78": {
629
+ "content": "<unused71>",
630
+ "lstrip": false,
631
+ "normalized": false,
632
+ "rstrip": false,
633
+ "single_word": false,
634
+ "special": false
635
+ },
636
+ "79": {
637
+ "content": "<unused72>",
638
+ "lstrip": false,
639
+ "normalized": false,
640
+ "rstrip": false,
641
+ "single_word": false,
642
+ "special": false
643
+ },
644
+ "80": {
645
+ "content": "<unused73>",
646
+ "lstrip": false,
647
+ "normalized": false,
648
+ "rstrip": false,
649
+ "single_word": false,
650
+ "special": false
651
+ },
652
+ "81": {
653
+ "content": "<unused74>",
654
+ "lstrip": false,
655
+ "normalized": false,
656
+ "rstrip": false,
657
+ "single_word": false,
658
+ "special": false
659
+ },
660
+ "82": {
661
+ "content": "<unused75>",
662
+ "lstrip": false,
663
+ "normalized": false,
664
+ "rstrip": false,
665
+ "single_word": false,
666
+ "special": false
667
+ },
668
+ "83": {
669
+ "content": "<unused76>",
670
+ "lstrip": false,
671
+ "normalized": false,
672
+ "rstrip": false,
673
+ "single_word": false,
674
+ "special": false
675
+ },
676
+ "84": {
677
+ "content": "<unused77>",
678
+ "lstrip": false,
679
+ "normalized": false,
680
+ "rstrip": false,
681
+ "single_word": false,
682
+ "special": false
683
+ },
684
+ "85": {
685
+ "content": "<unused78>",
686
+ "lstrip": false,
687
+ "normalized": false,
688
+ "rstrip": false,
689
+ "single_word": false,
690
+ "special": false
691
+ },
692
+ "86": {
693
+ "content": "<unused79>",
694
+ "lstrip": false,
695
+ "normalized": false,
696
+ "rstrip": false,
697
+ "single_word": false,
698
+ "special": false
699
+ },
700
+ "87": {
701
+ "content": "<unused80>",
702
+ "lstrip": false,
703
+ "normalized": false,
704
+ "rstrip": false,
705
+ "single_word": false,
706
+ "special": false
707
+ },
708
+ "88": {
709
+ "content": "<unused81>",
710
+ "lstrip": false,
711
+ "normalized": false,
712
+ "rstrip": false,
713
+ "single_word": false,
714
+ "special": false
715
+ },
716
+ "89": {
717
+ "content": "<unused82>",
718
+ "lstrip": false,
719
+ "normalized": false,
720
+ "rstrip": false,
721
+ "single_word": false,
722
+ "special": false
723
+ },
724
+ "90": {
725
+ "content": "<unused83>",
726
+ "lstrip": false,
727
+ "normalized": false,
728
+ "rstrip": false,
729
+ "single_word": false,
730
+ "special": false
731
+ },
732
+ "91": {
733
+ "content": "<unused84>",
734
+ "lstrip": false,
735
+ "normalized": false,
736
+ "rstrip": false,
737
+ "single_word": false,
738
+ "special": false
739
+ },
740
+ "92": {
741
+ "content": "<unused85>",
742
+ "lstrip": false,
743
+ "normalized": false,
744
+ "rstrip": false,
745
+ "single_word": false,
746
+ "special": false
747
+ },
748
+ "93": {
749
+ "content": "<unused86>",
750
+ "lstrip": false,
751
+ "normalized": false,
752
+ "rstrip": false,
753
+ "single_word": false,
754
+ "special": false
755
+ },
756
+ "94": {
757
+ "content": "<unused87>",
758
+ "lstrip": false,
759
+ "normalized": false,
760
+ "rstrip": false,
761
+ "single_word": false,
762
+ "special": false
763
+ },
764
+ "95": {
765
+ "content": "<unused88>",
766
+ "lstrip": false,
767
+ "normalized": false,
768
+ "rstrip": false,
769
+ "single_word": false,
770
+ "special": false
771
+ },
772
+ "96": {
773
+ "content": "<unused89>",
774
+ "lstrip": false,
775
+ "normalized": false,
776
+ "rstrip": false,
777
+ "single_word": false,
778
+ "special": false
779
+ },
780
+ "97": {
781
+ "content": "<unused90>",
782
+ "lstrip": false,
783
+ "normalized": false,
784
+ "rstrip": false,
785
+ "single_word": false,
786
+ "special": false
787
+ },
788
+ "98": {
789
+ "content": "<unused91>",
790
+ "lstrip": false,
791
+ "normalized": false,
792
+ "rstrip": false,
793
+ "single_word": false,
794
+ "special": false
795
+ },
796
+ "99": {
797
+ "content": "<unused92>",
798
+ "lstrip": false,
799
+ "normalized": false,
800
+ "rstrip": false,
801
+ "single_word": false,
802
+ "special": false
803
+ },
804
+ "100": {
805
+ "content": "<unused93>",
806
+ "lstrip": false,
807
+ "normalized": false,
808
+ "rstrip": false,
809
+ "single_word": false,
810
+ "special": false
811
+ },
812
+ "101": {
813
+ "content": "<unused94>",
814
+ "lstrip": false,
815
+ "normalized": false,
816
+ "rstrip": false,
817
+ "single_word": false,
818
+ "special": false
819
+ },
820
+ "102": {
821
+ "content": "<unused95>",
822
+ "lstrip": false,
823
+ "normalized": false,
824
+ "rstrip": false,
825
+ "single_word": false,
826
+ "special": false
827
+ },
828
+ "103": {
829
+ "content": "<unused96>",
830
+ "lstrip": false,
831
+ "normalized": false,
832
+ "rstrip": false,
833
+ "single_word": false,
834
+ "special": false
835
+ },
836
+ "104": {
837
+ "content": "<unused97>",
838
+ "lstrip": false,
839
+ "normalized": false,
840
+ "rstrip": false,
841
+ "single_word": false,
842
+ "special": false
843
+ },
844
+ "105": {
845
+ "content": "<unused98>",
846
+ "lstrip": false,
847
+ "normalized": false,
848
+ "rstrip": false,
849
+ "single_word": false,
850
+ "special": false
851
+ },
852
+ "106": {
853
+ "content": "<start_of_turn>",
854
+ "lstrip": false,
855
+ "normalized": false,
856
+ "rstrip": false,
857
+ "single_word": false,
858
+ "special": true
859
+ },
860
+ "107": {
861
+ "content": "<end_of_turn>",
862
+ "lstrip": false,
863
+ "normalized": false,
864
+ "rstrip": false,
865
+ "single_word": false,
866
+ "special": true
867
+ },
868
+ "108": {
869
+ "content": "\n",
870
+ "lstrip": false,
871
+ "normalized": false,
872
+ "rstrip": false,
873
+ "single_word": false,
874
+ "special": false
875
+ },
876
+ "109": {
877
+ "content": "\n\n",
878
+ "lstrip": false,
879
+ "normalized": false,
880
+ "rstrip": false,
881
+ "single_word": false,
882
+ "special": false
883
+ },
884
+ "110": {
885
+ "content": "\n\n\n",
886
+ "lstrip": false,
887
+ "normalized": false,
888
+ "rstrip": false,
889
+ "single_word": false,
890
+ "special": false
891
+ },
892
+ "111": {
893
+ "content": "\n\n\n\n",
894
+ "lstrip": false,
895
+ "normalized": false,
896
+ "rstrip": false,
897
+ "single_word": false,
898
+ "special": false
899
+ },
900
+ "112": {
901
+ "content": "\n\n\n\n\n",
902
+ "lstrip": false,
903
+ "normalized": false,
904
+ "rstrip": false,
905
+ "single_word": false,
906
+ "special": false
907
+ },
908
+ "113": {
909
+ "content": "\n\n\n\n\n\n",
910
+ "lstrip": false,
911
+ "normalized": false,
912
+ "rstrip": false,
913
+ "single_word": false,
914
+ "special": false
915
+ },
916
+ "114": {
917
+ "content": "\n\n\n\n\n\n\n",
918
+ "lstrip": false,
919
+ "normalized": false,
920
+ "rstrip": false,
921
+ "single_word": false,
922
+ "special": false
923
+ },
924
+ "115": {
925
+ "content": "\n\n\n\n\n\n\n\n",
926
+ "lstrip": false,
927
+ "normalized": false,
928
+ "rstrip": false,
929
+ "single_word": false,
930
+ "special": false
931
+ },
932
+ "116": {
933
+ "content": "\n\n\n\n\n\n\n\n\n",
934
+ "lstrip": false,
935
+ "normalized": false,
936
+ "rstrip": false,
937
+ "single_word": false,
938
+ "special": false
939
+ },
940
+ "117": {
941
+ "content": "\n\n\n\n\n\n\n\n\n\n",
942
+ "lstrip": false,
943
+ "normalized": false,
944
+ "rstrip": false,
945
+ "single_word": false,
946
+ "special": false
947
+ },
948
+ "118": {
949
+ "content": "\n\n\n\n\n\n\n\n\n\n\n",
950
+ "lstrip": false,
951
+ "normalized": false,
952
+ "rstrip": false,
953
+ "single_word": false,
954
+ "special": false
955
+ },
956
+ "119": {
957
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n",
958
+ "lstrip": false,
959
+ "normalized": false,
960
+ "rstrip": false,
961
+ "single_word": false,
962
+ "special": false
963
+ },
964
+ "120": {
965
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n",
966
+ "lstrip": false,
967
+ "normalized": false,
968
+ "rstrip": false,
969
+ "single_word": false,
970
+ "special": false
971
+ },
972
+ "121": {
973
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
974
+ "lstrip": false,
975
+ "normalized": false,
976
+ "rstrip": false,
977
+ "single_word": false,
978
+ "special": false
979
+ },
980
+ "122": {
981
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
982
+ "lstrip": false,
983
+ "normalized": false,
984
+ "rstrip": false,
985
+ "single_word": false,
986
+ "special": false
987
+ },
988
+ "123": {
989
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
990
+ "lstrip": false,
991
+ "normalized": false,
992
+ "rstrip": false,
993
+ "single_word": false,
994
+ "special": false
995
+ },
996
+ "124": {
997
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
998
+ "lstrip": false,
999
+ "normalized": false,
1000
+ "rstrip": false,
1001
+ "single_word": false,
1002
+ "special": false
1003
+ },
1004
+ "125": {
1005
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1006
+ "lstrip": false,
1007
+ "normalized": false,
1008
+ "rstrip": false,
1009
+ "single_word": false,
1010
+ "special": false
1011
+ },
1012
+ "126": {
1013
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1014
+ "lstrip": false,
1015
+ "normalized": false,
1016
+ "rstrip": false,
1017
+ "single_word": false,
1018
+ "special": false
1019
+ },
1020
+ "127": {
1021
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1022
+ "lstrip": false,
1023
+ "normalized": false,
1024
+ "rstrip": false,
1025
+ "single_word": false,
1026
+ "special": false
1027
+ },
1028
+ "128": {
1029
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1030
+ "lstrip": false,
1031
+ "normalized": false,
1032
+ "rstrip": false,
1033
+ "single_word": false,
1034
+ "special": false
1035
+ },
1036
+ "129": {
1037
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1038
+ "lstrip": false,
1039
+ "normalized": false,
1040
+ "rstrip": false,
1041
+ "single_word": false,
1042
+ "special": false
1043
+ },
1044
+ "130": {
1045
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1046
+ "lstrip": false,
1047
+ "normalized": false,
1048
+ "rstrip": false,
1049
+ "single_word": false,
1050
+ "special": false
1051
+ },
1052
+ "131": {
1053
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1054
+ "lstrip": false,
1055
+ "normalized": false,
1056
+ "rstrip": false,
1057
+ "single_word": false,
1058
+ "special": false
1059
+ },
1060
+ "132": {
1061
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1062
+ "lstrip": false,
1063
+ "normalized": false,
1064
+ "rstrip": false,
1065
+ "single_word": false,
1066
+ "special": false
1067
+ },
1068
+ "133": {
1069
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1070
+ "lstrip": false,
1071
+ "normalized": false,
1072
+ "rstrip": false,
1073
+ "single_word": false,
1074
+ "special": false
1075
+ },
1076
+ "134": {
1077
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1078
+ "lstrip": false,
1079
+ "normalized": false,
1080
+ "rstrip": false,
1081
+ "single_word": false,
1082
+ "special": false
1083
+ },
1084
+ "135": {
1085
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1086
+ "lstrip": false,
1087
+ "normalized": false,
1088
+ "rstrip": false,
1089
+ "single_word": false,
1090
+ "special": false
1091
+ },
1092
+ "136": {
1093
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1094
+ "lstrip": false,
1095
+ "normalized": false,
1096
+ "rstrip": false,
1097
+ "single_word": false,
1098
+ "special": false
1099
+ },
1100
+ "137": {
1101
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1102
+ "lstrip": false,
1103
+ "normalized": false,
1104
+ "rstrip": false,
1105
+ "single_word": false,
1106
+ "special": false
1107
+ },
1108
+ "138": {
1109
+ "content": "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n",
1110
+ "lstrip": false,
1111
+ "normalized": false,
1112
+ "rstrip": false,
1113
+ "single_word": false,
1114
+ "special": false
1115
+ },
1116
+ "139": {
1117
+ "content": "▁▁",
1118
+ "lstrip": false,
1119
+ "normalized": false,
1120
+ "rstrip": false,
1121
+ "single_word": false,
1122
+ "special": false
1123
+ },
1124
+ "140": {
1125
+ "content": "▁▁▁",
1126
+ "lstrip": false,
1127
+ "normalized": false,
1128
+ "rstrip": false,
1129
+ "single_word": false,
1130
+ "special": false
1131
+ },
1132
+ "141": {
1133
+ "content": "▁▁▁▁",
1134
+ "lstrip": false,
1135
+ "normalized": false,
1136
+ "rstrip": false,
1137
+ "single_word": false,
1138
+ "special": false
1139
+ },
1140
+ "142": {
1141
+ "content": "▁▁▁▁▁",
1142
+ "lstrip": false,
1143
+ "normalized": false,
1144
+ "rstrip": false,
1145
+ "single_word": false,
1146
+ "special": false
1147
+ },
1148
+ "143": {
1149
+ "content": "▁▁▁▁▁▁",
1150
+ "lstrip": false,
1151
+ "normalized": false,
1152
+ "rstrip": false,
1153
+ "single_word": false,
1154
+ "special": false
1155
+ },
1156
+ "144": {
1157
+ "content": "▁▁▁▁▁▁▁",
1158
+ "lstrip": false,
1159
+ "normalized": false,
1160
+ "rstrip": false,
1161
+ "single_word": false,
1162
+ "special": false
1163
+ },
1164
+ "145": {
1165
+ "content": "▁▁▁▁▁▁▁▁",
1166
+ "lstrip": false,
1167
+ "normalized": false,
1168
+ "rstrip": false,
1169
+ "single_word": false,
1170
+ "special": false
1171
+ },
1172
+ "146": {
1173
+ "content": "▁▁▁▁▁▁▁▁▁",
1174
+ "lstrip": false,
1175
+ "normalized": false,
1176
+ "rstrip": false,
1177
+ "single_word": false,
1178
+ "special": false
1179
+ },
1180
+ "147": {
1181
+ "content": "▁▁▁▁▁▁▁▁▁▁",
1182
+ "lstrip": false,
1183
+ "normalized": false,
1184
+ "rstrip": false,
1185
+ "single_word": false,
1186
+ "special": false
1187
+ },
1188
+ "148": {
1189
+ "content": "▁▁▁▁▁▁▁▁▁▁▁",
1190
+ "lstrip": false,
1191
+ "normalized": false,
1192
+ "rstrip": false,
1193
+ "single_word": false,
1194
+ "special": false
1195
+ },
1196
+ "149": {
1197
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁",
1198
+ "lstrip": false,
1199
+ "normalized": false,
1200
+ "rstrip": false,
1201
+ "single_word": false,
1202
+ "special": false
1203
+ },
1204
+ "150": {
1205
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁",
1206
+ "lstrip": false,
1207
+ "normalized": false,
1208
+ "rstrip": false,
1209
+ "single_word": false,
1210
+ "special": false
1211
+ },
1212
+ "151": {
1213
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1214
+ "lstrip": false,
1215
+ "normalized": false,
1216
+ "rstrip": false,
1217
+ "single_word": false,
1218
+ "special": false
1219
+ },
1220
+ "152": {
1221
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1222
+ "lstrip": false,
1223
+ "normalized": false,
1224
+ "rstrip": false,
1225
+ "single_word": false,
1226
+ "special": false
1227
+ },
1228
+ "153": {
1229
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1230
+ "lstrip": false,
1231
+ "normalized": false,
1232
+ "rstrip": false,
1233
+ "single_word": false,
1234
+ "special": false
1235
+ },
1236
+ "154": {
1237
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1238
+ "lstrip": false,
1239
+ "normalized": false,
1240
+ "rstrip": false,
1241
+ "single_word": false,
1242
+ "special": false
1243
+ },
1244
+ "155": {
1245
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1246
+ "lstrip": false,
1247
+ "normalized": false,
1248
+ "rstrip": false,
1249
+ "single_word": false,
1250
+ "special": false
1251
+ },
1252
+ "156": {
1253
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1254
+ "lstrip": false,
1255
+ "normalized": false,
1256
+ "rstrip": false,
1257
+ "single_word": false,
1258
+ "special": false
1259
+ },
1260
+ "157": {
1261
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1262
+ "lstrip": false,
1263
+ "normalized": false,
1264
+ "rstrip": false,
1265
+ "single_word": false,
1266
+ "special": false
1267
+ },
1268
+ "158": {
1269
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1270
+ "lstrip": false,
1271
+ "normalized": false,
1272
+ "rstrip": false,
1273
+ "single_word": false,
1274
+ "special": false
1275
+ },
1276
+ "159": {
1277
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1278
+ "lstrip": false,
1279
+ "normalized": false,
1280
+ "rstrip": false,
1281
+ "single_word": false,
1282
+ "special": false
1283
+ },
1284
+ "160": {
1285
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1286
+ "lstrip": false,
1287
+ "normalized": false,
1288
+ "rstrip": false,
1289
+ "single_word": false,
1290
+ "special": false
1291
+ },
1292
+ "161": {
1293
+ "content": "▁▁▁���▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1294
+ "lstrip": false,
1295
+ "normalized": false,
1296
+ "rstrip": false,
1297
+ "single_word": false,
1298
+ "special": false
1299
+ },
1300
+ "162": {
1301
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1302
+ "lstrip": false,
1303
+ "normalized": false,
1304
+ "rstrip": false,
1305
+ "single_word": false,
1306
+ "special": false
1307
+ },
1308
+ "163": {
1309
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1310
+ "lstrip": false,
1311
+ "normalized": false,
1312
+ "rstrip": false,
1313
+ "single_word": false,
1314
+ "special": false
1315
+ },
1316
+ "164": {
1317
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1318
+ "lstrip": false,
1319
+ "normalized": false,
1320
+ "rstrip": false,
1321
+ "single_word": false,
1322
+ "special": false
1323
+ },
1324
+ "165": {
1325
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1326
+ "lstrip": false,
1327
+ "normalized": false,
1328
+ "rstrip": false,
1329
+ "single_word": false,
1330
+ "special": false
1331
+ },
1332
+ "166": {
1333
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1334
+ "lstrip": false,
1335
+ "normalized": false,
1336
+ "rstrip": false,
1337
+ "single_word": false,
1338
+ "special": false
1339
+ },
1340
+ "167": {
1341
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1342
+ "lstrip": false,
1343
+ "normalized": false,
1344
+ "rstrip": false,
1345
+ "single_word": false,
1346
+ "special": false
1347
+ },
1348
+ "168": {
1349
+ "content": "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁",
1350
+ "lstrip": false,
1351
+ "normalized": false,
1352
+ "rstrip": false,
1353
+ "single_word": false,
1354
+ "special": false
1355
+ },
1356
+ "169": {
1357
+ "content": "<table>",
1358
+ "lstrip": false,
1359
+ "normalized": false,
1360
+ "rstrip": false,
1361
+ "single_word": false,
1362
+ "special": false
1363
+ },
1364
+ "170": {
1365
+ "content": "<caption>",
1366
+ "lstrip": false,
1367
+ "normalized": false,
1368
+ "rstrip": false,
1369
+ "single_word": false,
1370
+ "special": false
1371
+ },
1372
+ "171": {
1373
+ "content": "<thead>",
1374
+ "lstrip": false,
1375
+ "normalized": false,
1376
+ "rstrip": false,
1377
+ "single_word": false,
1378
+ "special": false
1379
+ },
1380
+ "172": {
1381
+ "content": "<tbody>",
1382
+ "lstrip": false,
1383
+ "normalized": false,
1384
+ "rstrip": false,
1385
+ "single_word": false,
1386
+ "special": false
1387
+ },
1388
+ "173": {
1389
+ "content": "<tfoot>",
1390
+ "lstrip": false,
1391
+ "normalized": false,
1392
+ "rstrip": false,
1393
+ "single_word": false,
1394
+ "special": false
1395
+ },
1396
+ "174": {
1397
+ "content": "<tr>",
1398
+ "lstrip": false,
1399
+ "normalized": false,
1400
+ "rstrip": false,
1401
+ "single_word": false,
1402
+ "special": false
1403
+ },
1404
+ "175": {
1405
+ "content": "<th>",
1406
+ "lstrip": false,
1407
+ "normalized": false,
1408
+ "rstrip": false,
1409
+ "single_word": false,
1410
+ "special": false
1411
+ },
1412
+ "176": {
1413
+ "content": "<td>",
1414
+ "lstrip": false,
1415
+ "normalized": false,
1416
+ "rstrip": false,
1417
+ "single_word": false,
1418
+ "special": false
1419
+ },
1420
+ "177": {
1421
+ "content": "</table>",
1422
+ "lstrip": false,
1423
+ "normalized": false,
1424
+ "rstrip": false,
1425
+ "single_word": false,
1426
+ "special": false
1427
+ },
1428
+ "178": {
1429
+ "content": "</caption>",
1430
+ "lstrip": false,
1431
+ "normalized": false,
1432
+ "rstrip": false,
1433
+ "single_word": false,
1434
+ "special": false
1435
+ },
1436
+ "179": {
1437
+ "content": "</thead>",
1438
+ "lstrip": false,
1439
+ "normalized": false,
1440
+ "rstrip": false,
1441
+ "single_word": false,
1442
+ "special": false
1443
+ },
1444
+ "180": {
1445
+ "content": "</tbody>",
1446
+ "lstrip": false,
1447
+ "normalized": false,
1448
+ "rstrip": false,
1449
+ "single_word": false,
1450
+ "special": false
1451
+ },
1452
+ "181": {
1453
+ "content": "</tfoot>",
1454
+ "lstrip": false,
1455
+ "normalized": false,
1456
+ "rstrip": false,
1457
+ "single_word": false,
1458
+ "special": false
1459
+ },
1460
+ "182": {
1461
+ "content": "</tr>",
1462
+ "lstrip": false,
1463
+ "normalized": false,
1464
+ "rstrip": false,
1465
+ "single_word": false,
1466
+ "special": false
1467
+ },
1468
+ "183": {
1469
+ "content": "</th>",
1470
+ "lstrip": false,
1471
+ "normalized": false,
1472
+ "rstrip": false,
1473
+ "single_word": false,
1474
+ "special": false
1475
+ },
1476
+ "184": {
1477
+ "content": "</td>",
1478
+ "lstrip": false,
1479
+ "normalized": false,
1480
+ "rstrip": false,
1481
+ "single_word": false,
1482
+ "special": false
1483
+ },
1484
+ "185": {
1485
+ "content": "<h1>",
1486
+ "lstrip": false,
1487
+ "normalized": false,
1488
+ "rstrip": false,
1489
+ "single_word": false,
1490
+ "special": false
1491
+ },
1492
+ "186": {
1493
+ "content": "<h2>",
1494
+ "lstrip": false,
1495
+ "normalized": false,
1496
+ "rstrip": false,
1497
+ "single_word": false,
1498
+ "special": false
1499
+ },
1500
+ "187": {
1501
+ "content": "<h3>",
1502
+ "lstrip": false,
1503
+ "normalized": false,
1504
+ "rstrip": false,
1505
+ "single_word": false,
1506
+ "special": false
1507
+ },
1508
+ "188": {
1509
+ "content": "<h4>",
1510
+ "lstrip": false,
1511
+ "normalized": false,
1512
+ "rstrip": false,
1513
+ "single_word": false,
1514
+ "special": false
1515
+ },
1516
+ "189": {
1517
+ "content": "<h5>",
1518
+ "lstrip": false,
1519
+ "normalized": false,
1520
+ "rstrip": false,
1521
+ "single_word": false,
1522
+ "special": false
1523
+ },
1524
+ "190": {
1525
+ "content": "<h6>",
1526
+ "lstrip": false,
1527
+ "normalized": false,
1528
+ "rstrip": false,
1529
+ "single_word": false,
1530
+ "special": false
1531
+ },
1532
+ "191": {
1533
+ "content": "<blockquote>",
1534
+ "lstrip": false,
1535
+ "normalized": false,
1536
+ "rstrip": false,
1537
+ "single_word": false,
1538
+ "special": false
1539
+ },
1540
+ "192": {
1541
+ "content": "</h1>",
1542
+ "lstrip": false,
1543
+ "normalized": false,
1544
+ "rstrip": false,
1545
+ "single_word": false,
1546
+ "special": false
1547
+ },
1548
+ "193": {
1549
+ "content": "</h2>",
1550
+ "lstrip": false,
1551
+ "normalized": false,
1552
+ "rstrip": false,
1553
+ "single_word": false,
1554
+ "special": false
1555
+ },
1556
+ "194": {
1557
+ "content": "</h3>",
1558
+ "lstrip": false,
1559
+ "normalized": false,
1560
+ "rstrip": false,
1561
+ "single_word": false,
1562
+ "special": false
1563
+ },
1564
+ "195": {
1565
+ "content": "</h4>",
1566
+ "lstrip": false,
1567
+ "normalized": false,
1568
+ "rstrip": false,
1569
+ "single_word": false,
1570
+ "special": false
1571
+ },
1572
+ "196": {
1573
+ "content": "</h5>",
1574
+ "lstrip": false,
1575
+ "normalized": false,
1576
+ "rstrip": false,
1577
+ "single_word": false,
1578
+ "special": false
1579
+ },
1580
+ "197": {
1581
+ "content": "</h6>",
1582
+ "lstrip": false,
1583
+ "normalized": false,
1584
+ "rstrip": false,
1585
+ "single_word": false,
1586
+ "special": false
1587
+ },
1588
+ "198": {
1589
+ "content": "</blockquote>",
1590
+ "lstrip": false,
1591
+ "normalized": false,
1592
+ "rstrip": false,
1593
+ "single_word": false,
1594
+ "special": false
1595
+ },
1596
+ "199": {
1597
+ "content": "<strong>",
1598
+ "lstrip": false,
1599
+ "normalized": false,
1600
+ "rstrip": false,
1601
+ "single_word": false,
1602
+ "special": false
1603
+ },
1604
+ "200": {
1605
+ "content": "<em>",
1606
+ "lstrip": false,
1607
+ "normalized": false,
1608
+ "rstrip": false,
1609
+ "single_word": false,
1610
+ "special": false
1611
+ },
1612
+ "201": {
1613
+ "content": "<b>",
1614
+ "lstrip": false,
1615
+ "normalized": false,
1616
+ "rstrip": false,
1617
+ "single_word": false,
1618
+ "special": false
1619
+ },
1620
+ "202": {
1621
+ "content": "<i>",
1622
+ "lstrip": false,
1623
+ "normalized": false,
1624
+ "rstrip": false,
1625
+ "single_word": false,
1626
+ "special": false
1627
+ },
1628
+ "203": {
1629
+ "content": "<u>",
1630
+ "lstrip": false,
1631
+ "normalized": false,
1632
+ "rstrip": false,
1633
+ "single_word": false,
1634
+ "special": false
1635
+ },
1636
+ "204": {
1637
+ "content": "<s>",
1638
+ "lstrip": false,
1639
+ "normalized": false,
1640
+ "rstrip": false,
1641
+ "single_word": false,
1642
+ "special": false
1643
+ },
1644
+ "205": {
1645
+ "content": "<sub>",
1646
+ "lstrip": false,
1647
+ "normalized": false,
1648
+ "rstrip": false,
1649
+ "single_word": false,
1650
+ "special": false
1651
+ },
1652
+ "206": {
1653
+ "content": "<sup>",
1654
+ "lstrip": false,
1655
+ "normalized": false,
1656
+ "rstrip": false,
1657
+ "single_word": false,
1658
+ "special": false
1659
+ },
1660
+ "207": {
1661
+ "content": "<code>",
1662
+ "lstrip": false,
1663
+ "normalized": false,
1664
+ "rstrip": false,
1665
+ "single_word": false,
1666
+ "special": false
1667
+ },
1668
+ "208": {
1669
+ "content": "</strong>",
1670
+ "lstrip": false,
1671
+ "normalized": false,
1672
+ "rstrip": false,
1673
+ "single_word": false,
1674
+ "special": false
1675
+ },
1676
+ "209": {
1677
+ "content": "</em>",
1678
+ "lstrip": false,
1679
+ "normalized": false,
1680
+ "rstrip": false,
1681
+ "single_word": false,
1682
+ "special": false
1683
+ },
1684
+ "210": {
1685
+ "content": "</b>",
1686
+ "lstrip": false,
1687
+ "normalized": false,
1688
+ "rstrip": false,
1689
+ "single_word": false,
1690
+ "special": false
1691
+ },
1692
+ "211": {
1693
+ "content": "</i>",
1694
+ "lstrip": false,
1695
+ "normalized": false,
1696
+ "rstrip": false,
1697
+ "single_word": false,
1698
+ "special": false
1699
+ },
1700
+ "212": {
1701
+ "content": "</u>",
1702
+ "lstrip": false,
1703
+ "normalized": false,
1704
+ "rstrip": false,
1705
+ "single_word": false,
1706
+ "special": false
1707
+ },
1708
+ "213": {
1709
+ "content": "</s>",
1710
+ "lstrip": false,
1711
+ "normalized": false,
1712
+ "rstrip": false,
1713
+ "single_word": false,
1714
+ "special": false
1715
+ },
1716
+ "214": {
1717
+ "content": "</sub>",
1718
+ "lstrip": false,
1719
+ "normalized": false,
1720
+ "rstrip": false,
1721
+ "single_word": false,
1722
+ "special": false
1723
+ },
1724
+ "215": {
1725
+ "content": "</sup>",
1726
+ "lstrip": false,
1727
+ "normalized": false,
1728
+ "rstrip": false,
1729
+ "single_word": false,
1730
+ "special": false
1731
+ },
1732
+ "216": {
1733
+ "content": "</code>",
1734
+ "lstrip": false,
1735
+ "normalized": false,
1736
+ "rstrip": false,
1737
+ "single_word": false,
1738
+ "special": false
1739
+ },
1740
+ "255968": {
1741
+ "content": "[toxicity=0]",
1742
+ "lstrip": false,
1743
+ "normalized": false,
1744
+ "rstrip": false,
1745
+ "single_word": false,
1746
+ "special": false
1747
+ },
1748
+ "255969": {
1749
+ "content": "\t\t",
1750
+ "lstrip": false,
1751
+ "normalized": false,
1752
+ "rstrip": false,
1753
+ "single_word": false,
1754
+ "special": false
1755
+ },
1756
+ "255970": {
1757
+ "content": "\t\t\t",
1758
+ "lstrip": false,
1759
+ "normalized": false,
1760
+ "rstrip": false,
1761
+ "single_word": false,
1762
+ "special": false
1763
+ },
1764
+ "255971": {
1765
+ "content": "\t\t\t\t",
1766
+ "lstrip": false,
1767
+ "normalized": false,
1768
+ "rstrip": false,
1769
+ "single_word": false,
1770
+ "special": false
1771
+ },
1772
+ "255972": {
1773
+ "content": "\t\t\t\t\t",
1774
+ "lstrip": false,
1775
+ "normalized": false,
1776
+ "rstrip": false,
1777
+ "single_word": false,
1778
+ "special": false
1779
+ },
1780
+ "255973": {
1781
+ "content": "\t\t\t\t\t\t",
1782
+ "lstrip": false,
1783
+ "normalized": false,
1784
+ "rstrip": false,
1785
+ "single_word": false,
1786
+ "special": false
1787
+ },
1788
+ "255974": {
1789
+ "content": "\t\t\t\t\t\t\t",
1790
+ "lstrip": false,
1791
+ "normalized": false,
1792
+ "rstrip": false,
1793
+ "single_word": false,
1794
+ "special": false
1795
+ },
1796
+ "255975": {
1797
+ "content": "\t\t\t\t\t\t\t\t",
1798
+ "lstrip": false,
1799
+ "normalized": false,
1800
+ "rstrip": false,
1801
+ "single_word": false,
1802
+ "special": false
1803
+ },
1804
+ "255976": {
1805
+ "content": "\t\t\t\t\t\t\t\t\t",
1806
+ "lstrip": false,
1807
+ "normalized": false,
1808
+ "rstrip": false,
1809
+ "single_word": false,
1810
+ "special": false
1811
+ },
1812
+ "255977": {
1813
+ "content": "\t\t\t\t\t\t\t\t\t\t",
1814
+ "lstrip": false,
1815
+ "normalized": false,
1816
+ "rstrip": false,
1817
+ "single_word": false,
1818
+ "special": false
1819
+ },
1820
+ "255978": {
1821
+ "content": "\t\t\t\t\t\t\t\t\t\t\t",
1822
+ "lstrip": false,
1823
+ "normalized": false,
1824
+ "rstrip": false,
1825
+ "single_word": false,
1826
+ "special": false
1827
+ },
1828
+ "255979": {
1829
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t",
1830
+ "lstrip": false,
1831
+ "normalized": false,
1832
+ "rstrip": false,
1833
+ "single_word": false,
1834
+ "special": false
1835
+ },
1836
+ "255980": {
1837
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t",
1838
+ "lstrip": false,
1839
+ "normalized": false,
1840
+ "rstrip": false,
1841
+ "single_word": false,
1842
+ "special": false
1843
+ },
1844
+ "255981": {
1845
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1846
+ "lstrip": false,
1847
+ "normalized": false,
1848
+ "rstrip": false,
1849
+ "single_word": false,
1850
+ "special": false
1851
+ },
1852
+ "255982": {
1853
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1854
+ "lstrip": false,
1855
+ "normalized": false,
1856
+ "rstrip": false,
1857
+ "single_word": false,
1858
+ "special": false
1859
+ },
1860
+ "255983": {
1861
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1862
+ "lstrip": false,
1863
+ "normalized": false,
1864
+ "rstrip": false,
1865
+ "single_word": false,
1866
+ "special": false
1867
+ },
1868
+ "255984": {
1869
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1870
+ "lstrip": false,
1871
+ "normalized": false,
1872
+ "rstrip": false,
1873
+ "single_word": false,
1874
+ "special": false
1875
+ },
1876
+ "255985": {
1877
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1878
+ "lstrip": false,
1879
+ "normalized": false,
1880
+ "rstrip": false,
1881
+ "single_word": false,
1882
+ "special": false
1883
+ },
1884
+ "255986": {
1885
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1886
+ "lstrip": false,
1887
+ "normalized": false,
1888
+ "rstrip": false,
1889
+ "single_word": false,
1890
+ "special": false
1891
+ },
1892
+ "255987": {
1893
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1894
+ "lstrip": false,
1895
+ "normalized": false,
1896
+ "rstrip": false,
1897
+ "single_word": false,
1898
+ "special": false
1899
+ },
1900
+ "255988": {
1901
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1902
+ "lstrip": false,
1903
+ "normalized": false,
1904
+ "rstrip": false,
1905
+ "single_word": false,
1906
+ "special": false
1907
+ },
1908
+ "255989": {
1909
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1910
+ "lstrip": false,
1911
+ "normalized": false,
1912
+ "rstrip": false,
1913
+ "single_word": false,
1914
+ "special": false
1915
+ },
1916
+ "255990": {
1917
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1918
+ "lstrip": false,
1919
+ "normalized": false,
1920
+ "rstrip": false,
1921
+ "single_word": false,
1922
+ "special": false
1923
+ },
1924
+ "255991": {
1925
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1926
+ "lstrip": false,
1927
+ "normalized": false,
1928
+ "rstrip": false,
1929
+ "single_word": false,
1930
+ "special": false
1931
+ },
1932
+ "255992": {
1933
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1934
+ "lstrip": false,
1935
+ "normalized": false,
1936
+ "rstrip": false,
1937
+ "single_word": false,
1938
+ "special": false
1939
+ },
1940
+ "255993": {
1941
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1942
+ "lstrip": false,
1943
+ "normalized": false,
1944
+ "rstrip": false,
1945
+ "single_word": false,
1946
+ "special": false
1947
+ },
1948
+ "255994": {
1949
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1950
+ "lstrip": false,
1951
+ "normalized": false,
1952
+ "rstrip": false,
1953
+ "single_word": false,
1954
+ "special": false
1955
+ },
1956
+ "255995": {
1957
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1958
+ "lstrip": false,
1959
+ "normalized": false,
1960
+ "rstrip": false,
1961
+ "single_word": false,
1962
+ "special": false
1963
+ },
1964
+ "255996": {
1965
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1966
+ "lstrip": false,
1967
+ "normalized": false,
1968
+ "rstrip": false,
1969
+ "single_word": false,
1970
+ "special": false
1971
+ },
1972
+ "255997": {
1973
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1974
+ "lstrip": false,
1975
+ "normalized": false,
1976
+ "rstrip": false,
1977
+ "single_word": false,
1978
+ "special": false
1979
+ },
1980
+ "255998": {
1981
+ "content": "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t",
1982
+ "lstrip": false,
1983
+ "normalized": false,
1984
+ "rstrip": false,
1985
+ "single_word": false,
1986
+ "special": false
1987
+ },
1988
+ "255999": {
1989
+ "content": "<unused99>",
1990
+ "lstrip": false,
1991
+ "normalized": false,
1992
+ "rstrip": false,
1993
+ "single_word": false,
1994
+ "special": false
1995
+ }
1996
+ },
1997
+ "additional_special_tokens": [
1998
+ "<start_of_turn>",
1999
+ "<end_of_turn>"
2000
+ ],
2001
+ "bos_token": "<bos>",
2002
+ "clean_up_tokenization_spaces": false,
2003
+ "cls_token": "<bos>",
2004
+ "eos_token": "<eos>",
2005
+ "extra_special_tokens": {},
2006
+ "mask_token": "<mask>",
2007
+ "model_input_names": [
2008
+ "input_ids",
2009
+ "attention_mask"
2010
+ ],
2011
+ "model_max_length": 32768,
2012
+ "pad_token": "<pad>",
2013
+ "padding_side": "right",
2014
+ "sep_token": "<eos>",
2015
+ "spaces_between_special_tokens": false,
2016
+ "tokenizer_class": "PreTrainedTokenizerFast",
2017
+ "unk_token": "<unk>"
2018
+ }
training-origin.json ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ {
2
+ "method": "full",
3
+ "base_model": "vllm-sr/Vela-1.0-Encoder-307M",
4
+ "base_revision": "720ab37904ce15054068d429381bfae7549f00f6"
5
+ }