Voltline commited on
Commit
2c87024
·
verified ·
1 Parent(s): f1b9f4f

Release frozen VimeML tiny-ja-v1 FP32 and Core ML INT8 (GPL-2.0)

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. LICENSE +339 -0
  2. README.md +112 -1
  3. RELEASE.json +481 -0
  4. SHA256SUMS.txt +118 -0
  5. coreml/ios18-int8-block32/manifest.json +491 -0
  6. coreml/ios18-int8-block32/model.mlpackage/Data/com.apple.CoreML/model.mlmodel +3 -0
  7. coreml/ios18-int8-block32/model.mlpackage/Data/com.apple.CoreML/weights/weight.bin +3 -0
  8. coreml/ios18-int8-block32/model.mlpackage/Manifest.json +18 -0
  9. evaluation/summary.json +239 -0
  10. infer.py +36 -0
  11. inference/config.json +9 -0
  12. inference/manifest.json +108 -0
  13. inference/token-manifest.json +119 -0
  14. inference/tokenizer.model +3 -0
  15. inference/weights.pt +3 -0
  16. source/LICENSE +339 -0
  17. source/README.md +3 -0
  18. source/configs/corpus-parallel.toml +35 -0
  19. source/configs/corpus-sharded.toml +35 -0
  20. source/configs/ime-eval-v1.json +54 -0
  21. source/configs/ime-homophones.json +18 -0
  22. source/configs/inference-prompts.json +14 -0
  23. source/configs/loader.toml +8 -0
  24. source/configs/phrase-demo-prompts.json +22 -0
  25. source/configs/tokenizer.toml +25 -0
  26. source/configs/train-smoke.toml +37 -0
  27. source/configs/train-v1.toml +40 -0
  28. source/requirements.txt +20 -0
  29. source/scripts/benchmarks/evaluate.py +11 -0
  30. source/scripts/benchmarks/evaluate_ajimee.py +11 -0
  31. source/scripts/benchmarks/export_cached_development.py +28 -0
  32. source/scripts/benchmarks/generate.py +11 -0
  33. source/scripts/benchmarks/generate_development.py +10 -0
  34. source/scripts/benchmarks/hybrid.py +10 -0
  35. source/scripts/benchmarks/prepare_ajimee.py +11 -0
  36. source/scripts/corpus/build.py +12 -0
  37. source/scripts/corpus/merge.py +33 -0
  38. source/scripts/corpus/preprocess.py +12 -0
  39. source/scripts/deployment/ane_output_probe.py +72 -0
  40. source/scripts/deployment/compare_quantization.py +119 -0
  41. source/scripts/deployment/compute_plan.py +57 -0
  42. source/scripts/deployment/coreml.py +11 -0
  43. source/scripts/deployment/coreml_ane.py +91 -0
  44. source/scripts/deployment/coreml_conservative.py +229 -0
  45. source/scripts/deployment/package_handoff.py +117 -0
  46. source/scripts/deployment/package_ios.py +76 -0
  47. source/scripts/deployment/summarize_keyboard_trace.py +77 -0
  48. source/scripts/deployment/summarize_memory.py +59 -0
  49. source/scripts/publishing/hub.py +50 -0
  50. source/scripts/publishing/prepare_hf.py +212 -0
LICENSE ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ <signature of Ty Coon>, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
README.md CHANGED
@@ -1,3 +1,114 @@
1
  ---
2
- license: apache-2.0
 
 
 
 
 
 
 
 
 
 
3
  ---
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  ---
2
+ language:
3
+ - ja
4
+ license: gpl-2.0
5
+ pipeline_tag: text-generation
6
+ tags:
7
+ - pytorch
8
+ - coreml
9
+ - japanese
10
+ - causal-lm
11
+ - contextual-reranking
12
+ - tiny-gpt
13
  ---
14
+
15
+ # VimeML Tiny Japanese GPT v1
16
+
17
+ A frozen **7,386,624-parameter**, decoder-only Japanese language model for local contextual candidate reranking and short within-sentence continuation. This repository provides the FP32 inference bundle and an iOS18 INT8-weight Core ML variant. SentencePiece tokenization, kana candidate retrieval, scoring and search run outside the Core ML graph.
18
+
19
+ This is a custom PyTorch model, not a Transformers `AutoModel` integration or an instruction/chat model. The source model, loader, scoring and conversion code are included under `source/`; no `trust_remote_code` or training checkpoint is needed.
20
+
21
+ ## Files and identity
22
+
23
+ | Path | Contents |
24
+ | --- | --- |
25
+ | `inference/` | Unique FP32 state_dict, config, original SentencePiece model, token manifest and provenance manifest; no optimizer |
26
+ | `coreml/ios18-int8-block32/` | Original Core ML `.mlpackage` and manifest |
27
+ | `source/` | Frozen VimeML Python source, conversion scripts, configuration and unified requirements |
28
+ | `infer.py` | Read-only wrapper around the original fingerprint-checked loader, generation and scoring |
29
+ | `evaluation/summary.json` | Curated results and source report hashes, without benchmark texts or device traces |
30
+ | `RELEASE.json`, `SHA256SUMS.txt`, `verify_release.py` | Release inventory and local byte verification |
31
+
32
+ - Source repository: [Voltline/VimeML](https://github.com/Voltline/VimeML), commit **9f0409ef7970c468efb622b207b1089de09651c1**.
33
+ - Inference manifest SHA256: `4284cdc5491513cc817633081677b363b14f59c0b4ffb78cdf7fcfb221f71b3c`.
34
+ - Core ML manifest SHA256: `a9d516233fa57aaadd09477e2e909760972ff49b5577919f8a441c489f5c2245`.
35
+ - Tokenizer SHA256: `d9f1ba1e456ce72dd9c06a62b12e804cf14090c30179e6078cd51d03063b2982`.
36
+ - Original training checkpoint SHA256: `93b6139aa05031df02038efcaf26788bc9916712919f3d2a4c4f398615cef75b`; the training checkpoint is not distributed here.
37
+
38
+ The original manifests remain byte-for-byte intact. Their old absolute paths describe provenance, not paths required on a downloader's machine. Core ML conversion scripts evolved after the original experiment; the old manifest's script hash is retained.
39
+
40
+ ## Architecture and tokenization
41
+
42
+ Pre-LayerNorm GPT with GELU, learned absolute positions and causal attention: vocabulary 16,384; context length 128; width 256; 4 layers; 4 heads; FFN width 1024. Input embedding and LM head share one parameter. There is no KV cache or sliding context. Each generation step recomputes the entire prefix.
43
+
44
+ SentencePiece 0.2.1 unigram, identity normalization, whitespace preservation and byte fallback. PAD/UNK/BOS/EOS IDs are 0/1/2/3. The exact original `tokenizer.model` is required; retraining a tokenizer does not preserve this vocabulary binding.
45
+
46
+ ## Quick start: downloaded files
47
+
48
+ Python ≥3.11. After downloading the repository, install the included unified requirements. Select the appropriate PyTorch build first if needed; do not use the PyTorch-only package index for the whole requirements file. The Darwin branch pins the original Core ML tool environment; no Core ML prediction occurs during FP32 inference.
49
+
50
+ ```sh
51
+ python -m pip install -r source/requirements.txt
52
+ python verify_release.py
53
+ python infer.py --prompt "今日は雨が降っているので、" --max-new-tokens 8
54
+ python infer.py --prompt "雨が降っているので、" --candidate "傘を持っていく" --candidate "橋を渡っていく"
55
+ ```
56
+
57
+ The second inference command ranks the supplied strings using the original contextual suffix logP sum. It does not perform kana conversion or dictionary lookup. Invalid/overlong candidates raise in this diagnostic wrapper; the application implementation retains the dictionary order on eligibility failure.
58
+
59
+ The FP32 loader checks all bundle files and the included source fingerprints, restores the shared head, and uses `torch.load(..., weights_only=True)`. Keep the source, tokenizer and inference files together. `verify_release.py` checks every release file without loading a model; checksums detect altered bytes but are not a digital signature.
60
+
61
+ ## Candidate scoring
62
+
63
+ Encode `context + candidate` jointly. Cancel the common token prefix across BOS+context and the jointly encoded candidate sequences, then sum original full-vocabulary log-softmax values over the remaining suffix. Do not add a candidate-final EOS. Ties preserve the original order. This token suffix likelihood is a scoring proxy, not exact string conditional probability.
64
+
65
+ Deployment uses **pure LM scoring**; AzooKey retrieves kana candidates. The older FP32 `AzooKey score + 2 × LM sum` policy is a historical comparison, not the INT8 deployment policy. Quantization does not establish that λ=2 remains optimal.
66
+
67
+ ## Training and evaluation
68
+
69
+ Training used nine local shards of [FineWeb2-Edu Japanese](https://huggingface.co/datasets/hotchpotch/fineweb-2-edu-japanese) and Japanese [Tatoeba](https://tatoeba.org/), cleaned, split into sentences, exactly deduplicated and grouped across 98%/1%/1% train/validation/test splits. There were 25,713,003 unique sentences and 25,185,368 training sentences. The tokenizer learned from 2 million sampled training sentences.
70
+
71
+ One data pass, 196,853 updates and 573,295,237 prediction targets. The frozen best checkpoint's complete validation loss is 4.5415708951, perplexity 93.8380942. Fixed-subset validation selected the checkpoint; the test split was not used for training, checkpoint selection or tuning. No test perplexity is claimed.
72
+
73
+ Reranking uses fixed N-best 20 AzooKey candidates, without injecting correct answers. [AJIMEE JWTD_v2/v1](https://github.com/azooKey/AJIMEE-Bench) has 200 cases and allows multiple accepted spellings. The synthetic development set has 137 reviewed cases.
74
+
75
+ | Frozen pool | AzooKey original Top-1 | FP32 pure LM Top-1 / Top-5 | INT8 pure LM Top-1 / Top-5 | Pool coverage |
76
+ | --- | ---: | ---: | ---: | ---: |
77
+ | Development | 111/137 | 122/137 / 134/137 | 122/137 / 134/137 | 136/137 |
78
+ | AJIMEE | 87/200 | 124/200 / 151/200 | 124/200 / 151/200 | 162/200 |
79
+
80
+ INT8 vs Windows FP32: development changes 119 complete rankings and 0 first choices; AJIMEE changes 163 complete rankings and 2 first choices, both between acceptable spellings. The original development compression report instead compares against uncompressed conservative Core ML: 118 complete rankings change. Equal hit counts do not mean equal scores or complete rankings. See the release summary for the separate baselines and hashes.
81
+
82
+ ## Core ML variant
83
+
84
+ **Weight-only symmetric INT8, block size 32; FP32 computation; CPU_ONLY; minimum iOS 18.** It is not INT8 activation or arithmetic inference. Logical `.mlpackage` size is **8,077,801 bytes**. This is not compiled size, IPA size or resident memory.
85
+
86
+ Input `input_ids`: INT32 `[1,T]`, 1≤T≤128. Output `logits`: FLOAT32 `[1,T,16384]`, full raw vocabulary logits. Only right-side PAD is supported. Shared embedding/head are retained. Use `.cpuOnly`; GPU/ALL paths for this package triggered a quantized-gather assertion in the client experiments.
87
+
88
+ On Mac, compile manually for the target platform before integrating:
89
+
90
+ ```sh
91
+ xcrun coremlcompiler compile coreml/ios18-int8-block32/model.mlpackage compiled-int8 --deployment-target 18.0
92
+ ```
93
+
94
+ The INT8 model **fails the strict FP32 logits alignment gate** (maximum absolute difference about 3.66). Candidate fixtures, causal and right-PAD checks passed, and the frozen pool hit counts above were preserved. Do not describe the model as numerically identical to FP32.
95
+
96
+ An AJIMEE-text diagnostic reported mean KL(FP32‖INT8)≈0.00589, mean NLL 5.56698→5.57289 and position top-choice agreement 94.4%. These are means of per-example position means, not a corpus token-weighted aggregate. Text content was capped at 127 tokens plus BOS; the old report did not record truncation counts. Similar aggregate KL to a simulated INT8 recipe does not prove exact conversion equivalence. Tested palette4 group16 degraded substantially and is not included.
97
+
98
+ ## Performance evidence and limits
99
+
100
+ Recorded iPhone 16 Pro Max optimized CPU_ONLY **test-host App** results: approximately 14 candidate reranking p50 10.4ms/p95 12.7ms; next-word p50 5.8ms; T128 prediction 3.0ms. Host memory increased by about 24.2MB and peaked at 74.8MB. These are not keyboard-extension latency or memory measurements.
101
+
102
+ A separate 152.7 second manual real-keyboard recording produced 127 approximately 1 second samples: physical footprint peak 21.25MiB and private resident peak 51.922MiB. The user confirmed LM ranking and next-word suggestions; installed build/model hashes were not independently captured. Short spikes may have been missed, the tail still grew slowly, and long-session stability is not certified. The different memory metrics cannot be substituted for one another. No energy claim is made.
103
+
104
+ ## Intended use and limitations
105
+
106
+ Japanese within-sentence reranking and short completion experiments, primarily for on-device input methods. The model is small, not instruction-tuned, and can repeat text, drift, truncate or invent facts. Outputs need product-level review; the fixed beam suite had only 10/20 prefixes with clearly natural suggestions in an AI review. These offline pool metrics do not measure application-wide accuracy or guarantee keyboard performance.
107
+
108
+ Semantic near-duplicates and training overlap with public evaluation text have not been comprehensively audited. AJIMEE has been used for error analysis and is not a fresh blind test. Development labels and review came from AI, not native-speaker adjudication. Official benchmark context can cross sentences, while the client uses current-sentence context. Candidate coverage limits reranking accuracy.
109
+
110
+ ## License and attribution
111
+
112
+ Model weights and the included VimeML code are released under **GNU GPL v2.0**, chosen by the author. See `LICENSE` and `source/LICENSE`. Training data and third-party software retain their own terms; they are not relicensed by this model repository.
113
+
114
+ FineWeb2-Edu Japanese's dataset card declares ODC-BY; [Tatoeba text terms](https://tatoeba.org/en/terms_of_use) default to CC-BY 2.0 FR with author attribution requirements. No corpus text is redistributed here. AJIMEE's source data is CC-BY-SA 3.0; this release distributes aggregate metrics, not its evaluation text. SentencePiece runtime code is not vendored into this release; installed dependencies retain their upstream licenses. Source configuration and training/conversion implementations are included, while datasets, API responses, optimizer state, device traces and the separate Vime client are excluded.
RELEASE.json ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": "vimeml_hf_release_v1",
3
+ "repo_id": "Voltline/vimeml-tiny-ja-v1",
4
+ "license": "gpl-2.0",
5
+ "source_repository": "https://github.com/Voltline/VimeML",
6
+ "source_commit": "9f0409ef7970c468efb622b207b1089de09651c1",
7
+ "created_utc": "2026-10-06T14:54:06.938207+00:00",
8
+ "bundle_manifest_sha256": "4284cdc5491513cc817633081677b363b14f59c0b4ffb78cdf7fcfb221f71b3c",
9
+ "coreml_manifest_sha256": "a9d516233fa57aaadd09477e2e909760972ff49b5577919f8a441c489f5c2245",
10
+ "files": {
11
+ "coreml/ios18-int8-block32/manifest.json": {
12
+ "bytes": 13273,
13
+ "sha256": "a9d516233fa57aaadd09477e2e909760972ff49b5577919f8a441c489f5c2245"
14
+ },
15
+ "coreml/ios18-int8-block32/model.mlpackage/Data/com.apple.CoreML/model.mlmodel": {
16
+ "bytes": 46400,
17
+ "sha256": "22e80193abc7672bbcf2a3fc214a186ae6e64d76c1e1b42ead38fef5a003cd37"
18
+ },
19
+ "coreml/ios18-int8-block32/model.mlpackage/Data/com.apple.CoreML/weights/weight.bin": {
20
+ "bytes": 8030784,
21
+ "sha256": "d3a063455371e624cf208fb6ed76fc9ac6addd8c2f87eaebf4653649f85b8109"
22
+ },
23
+ "coreml/ios18-int8-block32/model.mlpackage/Manifest.json": {
24
+ "bytes": 617,
25
+ "sha256": "31b13734848024036806cbd5bacf405eddba6c4451e5659b25f714ffe1766ee5"
26
+ },
27
+ "evaluation/summary.json": {
28
+ "bytes": 9055,
29
+ "sha256": "d0fe405a4f68c3e5f94541834ab9f60cf0937b4f2fd6c50d25737e6ab828ad0e"
30
+ },
31
+ "infer.py": {
32
+ "bytes": 1528,
33
+ "sha256": "8ed8d241d349a4e88840dd841da9db3191ec9f0db84c8f8165a951fa84720621"
34
+ },
35
+ "inference/config.json": {
36
+ "bytes": 145,
37
+ "sha256": "8950524f2f3a0373f3a0ebbcde79a8feaa54949ed926830c287b384306adf9ca"
38
+ },
39
+ "inference/manifest.json": {
40
+ "bytes": 4746,
41
+ "sha256": "4284cdc5491513cc817633081677b363b14f59c0b4ffb78cdf7fcfb221f71b3c"
42
+ },
43
+ "inference/token-manifest.json": {
44
+ "bytes": 6208,
45
+ "sha256": "c7ebf3672d46cfc75d12013c34f35fb7ac9eac613f29f3454be0cb0e991854dc"
46
+ },
47
+ "inference/tokenizer.model": {
48
+ "bytes": 284118,
49
+ "sha256": "d9f1ba1e456ce72dd9c06a62b12e804cf14090c30179e6078cd51d03063b2982"
50
+ },
51
+ "inference/weights.pt": {
52
+ "bytes": 29562119,
53
+ "sha256": "1cdac16913068bfdcfa94b139ecb34d945d0e9483a863373a6dca4397e3cd355"
54
+ },
55
+ "LICENSE": {
56
+ "bytes": 18431,
57
+ "sha256": "189b1af95d661151e054cea10c91b3d754e4de4d3fecfb074c1fb29476f7167b"
58
+ },
59
+ "README.md": {
60
+ "bytes": 10728,
61
+ "sha256": "366194211cc7db2f7de4f82471796b543f829e4c646a984758f2a5d589556f70"
62
+ },
63
+ "source/configs/corpus-parallel.toml": {
64
+ "bytes": 1239,
65
+ "sha256": "67e3bf5c3166c9c5fa847ec515c44dd41dfebee56134265df2c5b13bec14ad9a"
66
+ },
67
+ "source/configs/corpus-sharded.toml": {
68
+ "bytes": 1179,
69
+ "sha256": "49f06564931dc4be63f76cdde8cd3be78ddeb81a45cbada768e0a0ca8191a49d"
70
+ },
71
+ "source/configs/ime-eval-v1.json": {
72
+ "bytes": 8368,
73
+ "sha256": "46d0b4eba6293a2fa2e7ef16d9132c6320e9d2da80921d60aa939e90e35dc222"
74
+ },
75
+ "source/configs/ime-homophones.json": {
76
+ "bytes": 1195,
77
+ "sha256": "61c475fba63964b540e55be39d70e786886c059ce58fee94465517f648f021fe"
78
+ },
79
+ "source/configs/inference-prompts.json": {
80
+ "bytes": 685,
81
+ "sha256": "e701173b553617086ad2ab679313423eabb19e89d9f51fd31b321336584652ea"
82
+ },
83
+ "source/configs/loader.toml": {
84
+ "bytes": 249,
85
+ "sha256": "e8baa76da3e223bcb3b333f96994ef4e86eeecb62cea18503eb8f7ad9c4a74cf"
86
+ },
87
+ "source/configs/phrase-demo-prompts.json": {
88
+ "bytes": 1500,
89
+ "sha256": "b51e101d6a4bc34c4a375439191e4adbd63d511ccc62b49c4d5b758decee17ed"
90
+ },
91
+ "source/configs/tokenizer.toml": {
92
+ "bytes": 658,
93
+ "sha256": "28396838808e5745c4d2251e90e095d2a2e816033526bcb154d1c74c98dfe9b5"
94
+ },
95
+ "source/configs/train-smoke.toml": {
96
+ "bytes": 791,
97
+ "sha256": "2d386dd8efe0ffd0da6df319226daf7bab8948b1bcb2abc7afb59c45d98d28b3"
98
+ },
99
+ "source/configs/train-v1.toml": {
100
+ "bytes": 900,
101
+ "sha256": "44ba93246d406d5187c496ad2cf23d1533e6f581a8d6dc6f7fff03fca22b8323"
102
+ },
103
+ "source/LICENSE": {
104
+ "bytes": 18431,
105
+ "sha256": "189b1af95d661151e054cea10c91b3d754e4de4d3fecfb074c1fb29476f7167b"
106
+ },
107
+ "source/README.md": {
108
+ "bytes": 274,
109
+ "sha256": "5184082e7c857b4ab965d3c5b7366a0d5b56d98cdb5e40511dda6d0c2e298290"
110
+ },
111
+ "source/requirements.txt": {
112
+ "bytes": 968,
113
+ "sha256": "0fac5689cead9021745847b2753294a9a4d95c002a61762f7f25254db2a26489"
114
+ },
115
+ "source/scripts/benchmarks/evaluate.py": {
116
+ "bytes": 278,
117
+ "sha256": "34a2a24d14c0e8b20c44dafab720075cbc23261a7fdbe71091dff12200e7fc24"
118
+ },
119
+ "source/scripts/benchmarks/evaluate_ajimee.py": {
120
+ "bytes": 291,
121
+ "sha256": "a28fc24da1f8cbb6972b49dc0223361e16c7b093872d92df6bd52aabd00bd54a"
122
+ },
123
+ "source/scripts/benchmarks/export_cached_development.py": {
124
+ "bytes": 1024,
125
+ "sha256": "d328ff258e4a8fa83e51be099f5fa8fbf480f2d377579c475c355f9f01263bf6"
126
+ },
127
+ "source/scripts/benchmarks/generate.py": {
128
+ "bytes": 269,
129
+ "sha256": "de9866c56cd846073a3a3a82b1553ab737ccb911001d17de71839865057d0293"
130
+ },
131
+ "source/scripts/benchmarks/generate_development.py": {
132
+ "bytes": 279,
133
+ "sha256": "3b3b4192535a035bbf57e3f51f1fb16a0647b1a3fc060a179b7ae9fb1127cc41"
134
+ },
135
+ "source/scripts/benchmarks/hybrid.py": {
136
+ "bytes": 281,
137
+ "sha256": "64dbd0fcee7e294bd290bbf9f2057e81656ede4e0b328cdae710360da24716b3"
138
+ },
139
+ "source/scripts/benchmarks/prepare_ajimee.py": {
140
+ "bytes": 272,
141
+ "sha256": "3935e16a81c389c5a3dd74da6da38732ff32b668ea3c6d0484c52f017657706a"
142
+ },
143
+ "source/scripts/corpus/build.py": {
144
+ "bytes": 271,
145
+ "sha256": "0e4d753adf6cf97cc19b975e4f35d3cea41a4f519782f6b8eeea3309260486df"
146
+ },
147
+ "source/scripts/corpus/merge.py": {
148
+ "bytes": 1335,
149
+ "sha256": "dd6639d55b9aecdbf46d20fec98ab78422ff53ad14ec5a4a39d6a43e824cd0d6"
150
+ },
151
+ "source/scripts/corpus/preprocess.py": {
152
+ "bytes": 279,
153
+ "sha256": "f8181f1b055661a1097d2bf80654ac59af4cf6a8b74afecb8f44b40e3d94d4f8"
154
+ },
155
+ "source/scripts/deployment/ane_output_probe.py": {
156
+ "bytes": 3786,
157
+ "sha256": "e3e12fd3373119539eb3ed23eaccbc1457cafa7a22ae444dd4b187c118d883ce"
158
+ },
159
+ "source/scripts/deployment/compare_quantization.py": {
160
+ "bytes": 6534,
161
+ "sha256": "9a0761dbd3ece9b730ed0e52010ef965a8415b4320188c31e1863ab0c874d2fd"
162
+ },
163
+ "source/scripts/deployment/compute_plan.py": {
164
+ "bytes": 2759,
165
+ "sha256": "2aa26fb2f2b59a630cc9cafb7eb684f237c44c780c1ec820badbbd07d8f2bf41"
166
+ },
167
+ "source/scripts/deployment/coreml.py": {
168
+ "bytes": 278,
169
+ "sha256": "7f321ab43b41acd4765045a93edc5c3ec874dcb741541a3f7460d13f8c27f50e"
170
+ },
171
+ "source/scripts/deployment/coreml_ane.py": {
172
+ "bytes": 4674,
173
+ "sha256": "28c67b5b30c3ad93a46703dad62236b2cd81ec5478ebf2cd9974e7b336001871"
174
+ },
175
+ "source/scripts/deployment/coreml_conservative.py": {
176
+ "bytes": 14097,
177
+ "sha256": "61b290658385ee40325263be24b1a1dffdc18fe5bc494dd02a1bf93bf301b986"
178
+ },
179
+ "source/scripts/deployment/package_handoff.py": {
180
+ "bytes": 6406,
181
+ "sha256": "65db41ee70efb5409738ece23eb41750e695f1746e42b85314198d72710fa7a7"
182
+ },
183
+ "source/scripts/deployment/package_ios.py": {
184
+ "bytes": 4266,
185
+ "sha256": "1228adeb409e3cf095aa7890390307b9cd518c535fc23365f5a8ffc01b144448"
186
+ },
187
+ "source/scripts/deployment/summarize_keyboard_trace.py": {
188
+ "bytes": 4106,
189
+ "sha256": "0857bab4b1dcf81ec8b1ae5e680672cf512d5547aa3246c1e2a7ad7bfc0b79e5"
190
+ },
191
+ "source/scripts/deployment/summarize_memory.py": {
192
+ "bytes": 3339,
193
+ "sha256": "a798a4478ba907f0b4c88246c90d865063fa5c7c02f10456e7faceea7a7966ba"
194
+ },
195
+ "source/scripts/publishing/hub.py": {
196
+ "bytes": 2825,
197
+ "sha256": "bbd7f36ed4682bcdcb5e4c5d87b42733106dbaebf6f949fcd60cd498dc059274"
198
+ },
199
+ "source/scripts/publishing/prepare_hf.py": {
200
+ "bytes": 12512,
201
+ "sha256": "994c690a95f8d13d15a1ab1671185c8e2744537f48d667c176f9a3079b3630e9"
202
+ },
203
+ "source/scripts/review/approve.py": {
204
+ "bytes": 273,
205
+ "sha256": "555b275e74482c597a26b3cbc23ffb1648e636801bc5ddeccf7c25df176414d0"
206
+ },
207
+ "source/scripts/review/prepare.py": {
208
+ "bytes": 273,
209
+ "sha256": "77578100f9aaae50cabac68a85996dcf02479179f6d17fbf30368ea7aa70ab5f"
210
+ },
211
+ "source/scripts/review/run.py": {
212
+ "bytes": 269,
213
+ "sha256": "d18a127f4a08b84ac03a398f3c96c935a0b6275fe0e7b1af2f3e3d4d92ec49b1"
214
+ },
215
+ "source/scripts/review/run_multi_key.py": {
216
+ "bytes": 282,
217
+ "sha256": "702ba0084944c8db433f1d12f64e6933659965a9a125434cce80f916f63c6f47"
218
+ },
219
+ "source/scripts/tokenizer/encode.py": {
220
+ "bytes": 284,
221
+ "sha256": "46af106351c8835ba0e001295c4d2122c8c629d799c913feb8292d2e83100670"
222
+ },
223
+ "source/scripts/tokenizer/train.py": {
224
+ "bytes": 274,
225
+ "sha256": "1001c67f565711d91c59335b342ab1a3e3fdb40f7f5642a8a49ce9ac46a4823d"
226
+ },
227
+ "source/scripts/tools/check_corpus.py": {
228
+ "bytes": 282,
229
+ "sha256": "231f867355c0cd2806e0ee20378c9671443309f2716db9dbf1426fda6bbe9eb6"
230
+ },
231
+ "source/scripts/tools/inspect_data.py": {
232
+ "bytes": 350,
233
+ "sha256": "fd5a05f0c74e4d3bd95097a37be009422ec348c3fd3e36f1ca12740c02309760"
234
+ },
235
+ "source/scripts/tools/phrase_demo.py": {
236
+ "bytes": 274,
237
+ "sha256": "09d81377f63d9c967e444ffdaaec83a7228de8109c4492a5175b46a8590387bc"
238
+ },
239
+ "source/scripts/training/check_data.py": {
240
+ "bytes": 278,
241
+ "sha256": "fce755bfd04595f95d6a021c5d2533599d3463542239da198d65e4c26a716e0a"
242
+ },
243
+ "source/scripts/training/evaluate_ime.py": {
244
+ "bytes": 274,
245
+ "sha256": "81729525388462eb93ede97f917f0f6eae1ef6518c35d169e19dc745f37ffa4d"
246
+ },
247
+ "source/scripts/training/infer.py": {
248
+ "bytes": 252,
249
+ "sha256": "6f409f7761d543e65d5f4c8886c8f59e0929b4b299977a8439bd0d13623755f4"
250
+ },
251
+ "source/scripts/training/train.py": {
252
+ "bytes": 263,
253
+ "sha256": "56b5781f98b3440d23d1516d830e83356db3a0b04c9aab134368c2ef9cefb91c"
254
+ },
255
+ "source/scripts/training/train_wandb.py": {
256
+ "bytes": 3345,
257
+ "sha256": "db40a3f34315dcd409f0b22d8633c569f71f6807841fb2551a9ca423869ca149"
258
+ },
259
+ "source/src/vimeml/__init__.py": {
260
+ "bytes": 52,
261
+ "sha256": "ff0f42adee5b707f35ea0ba1307e65d8a2f632a55c3281c8f446211032f0fbc1"
262
+ },
263
+ "source/src/vimeml/benchmarks/__init__.py": {
264
+ "bytes": 77,
265
+ "sha256": "03a0b8a9e43ef3ecf56189a86fe7e33d9633d4a766edf02583e911aef9bde581"
266
+ },
267
+ "source/src/vimeml/benchmarks/ajimee.py": {
268
+ "bytes": 6566,
269
+ "sha256": "4916651168f3f883161ebbbc24db5e1ae5b1bc4cd6abf8c7b83560ef960406cb"
270
+ },
271
+ "source/src/vimeml/benchmarks/development.py": {
272
+ "bytes": 36457,
273
+ "sha256": "d74e8f9e888c115c6a9ce27f5b08ea40efb332c087d94e372d4c33364b2dc2d4"
274
+ },
275
+ "source/src/vimeml/benchmarks/evaluate.py": {
276
+ "bytes": 9097,
277
+ "sha256": "867cd2f41b49043dd985e7f05e788f3f83a0ac47a4d0d9b2d6306dcd0c96b02a"
278
+ },
279
+ "source/src/vimeml/benchmarks/evaluate_ajimee.py": {
280
+ "bytes": 13783,
281
+ "sha256": "459a8c0d1bb4f8ffba57e92ee2c8bb3f5278c3e4a1a5bcc5dfda48a21bde3716"
282
+ },
283
+ "source/src/vimeml/benchmarks/generate.py": {
284
+ "bytes": 28505,
285
+ "sha256": "9a468c23ea3ebf02f1184b1742cbe11a025f2a0d7ae57e0ee2e54468de72a1d7"
286
+ },
287
+ "source/src/vimeml/benchmarks/hybrid.py": {
288
+ "bytes": 20442,
289
+ "sha256": "71267f445ac92736f1dabf03c3e77193e9f2a1323f23fa1bc060aff4fbe4ea7e"
290
+ },
291
+ "source/src/vimeml/corpus/__init__.py": {
292
+ "bytes": 32,
293
+ "sha256": "55cdd3f899dd7ad156f8987cf2ba2bbbc76a95dfaba33a319058b4d01e2f9947"
294
+ },
295
+ "source/src/vimeml/corpus/build.py": {
296
+ "bytes": 12675,
297
+ "sha256": "895b6894c4b85e72c8655cf6b42e21d51b51736d649ec2adb6841e033f4ac1f9"
298
+ },
299
+ "source/src/vimeml/data/__init__.py": {
300
+ "bytes": 30,
301
+ "sha256": "86d655d2d715fcb2f73269c1ffbfc9e87d9080a1baaf389f88e30de6022fdc19"
302
+ },
303
+ "source/src/vimeml/data/annotations.py": {
304
+ "bytes": 6610,
305
+ "sha256": "ba62919c8084d24cb3f41c451e970b5448e7b0f5407340c28cca0b220d387395"
306
+ },
307
+ "source/src/vimeml/data/build.py": {
308
+ "bytes": 29679,
309
+ "sha256": "d7d63e1ea9a2f9da9fe92ff2773b792c7ca89911b2d7058a2709fcbbc443081e"
310
+ },
311
+ "source/src/vimeml/data/clean.py": {
312
+ "bytes": 1791,
313
+ "sha256": "ee88adbb74d5a03f591231484928becf0a62557c85702efddd214c9bc8a2354f"
314
+ },
315
+ "source/src/vimeml/data/corpus_export.py": {
316
+ "bytes": 8056,
317
+ "sha256": "d2e943aae1e2942076b1db8430067e048fcdc71d46761bb329f7fa460e843280"
318
+ },
319
+ "source/src/vimeml/data/corpus_parts.py": {
320
+ "bytes": 10178,
321
+ "sha256": "c196a78e5096e659eb8c536602e009c98de346bd82084c29f9384e526055f6f6"
322
+ },
323
+ "source/src/vimeml/data/merge_parts.py": {
324
+ "bytes": 8755,
325
+ "sha256": "43cb87845d17e52261675073b010269c53e4c79a8e5e16237d2627c9e07116e6"
326
+ },
327
+ "source/src/vimeml/data/merge_parts_fast.py": {
328
+ "bytes": 27413,
329
+ "sha256": "600dc56899dfa6ac2eda639cf79f6ad5e789642b99d6c68569262f593bfc3642"
330
+ },
331
+ "source/src/vimeml/data/preprocess_part.py": {
332
+ "bytes": 6553,
333
+ "sha256": "433a93f18809aa1b11a13c8e63edce2a73864140fbaf4a4bcff0c7ab475be164"
334
+ },
335
+ "source/src/vimeml/data/readers.py": {
336
+ "bytes": 4734,
337
+ "sha256": "837017b0282f62ed68c9a47c69da3ed12fe39b74e7f88bc3ae16113f55b679b6"
338
+ },
339
+ "source/src/vimeml/data/segment.py": {
340
+ "bytes": 8309,
341
+ "sha256": "9834f4867e126f229e86a7fc5b7481f4c803c05d00a21966a78ec293095b0f8c"
342
+ },
343
+ "source/src/vimeml/deployment/__init__.py": {
344
+ "bytes": 86,
345
+ "sha256": "9301bfc92623e4a8b2589e01d7ca98aac917152544b83562b23628bf4cfe414f"
346
+ },
347
+ "source/src/vimeml/deployment/bundle.py": {
348
+ "bytes": 7978,
349
+ "sha256": "4f66f215e576ea1267971da0f49d995f49b09417920cbbf9e159e2d1c8083043"
350
+ },
351
+ "source/src/vimeml/deployment/cli.py": {
352
+ "bytes": 6394,
353
+ "sha256": "55d65451e04721c3c05a131b9c422f2e7c1835d6340cc8b76c4936243521dd6a"
354
+ },
355
+ "source/src/vimeml/deployment/coreml.py": {
356
+ "bytes": 8328,
357
+ "sha256": "8281456e241e32ed79f9223d63f5c476e02b97b263723be9ad46c0a75435ee52"
358
+ },
359
+ "source/src/vimeml/deployment/graph.py": {
360
+ "bytes": 2306,
361
+ "sha256": "e2474a97e4006c4e1fe14552b11874d08c52fa217b41c9d0ab0dbab7b0a79a67"
362
+ },
363
+ "source/src/vimeml/deployment/validation.py": {
364
+ "bytes": 15542,
365
+ "sha256": "9e5d526dbd8e9be72f9d8782ac402623007226ee9e1ca14f8ce005a52a994c95"
366
+ },
367
+ "source/src/vimeml/review/__init__.py": {
368
+ "bytes": 32,
369
+ "sha256": "6422f9e2042a114155c7caf2721c1f6bd8e5de4d153009703af1281f2a1c76c3"
370
+ },
371
+ "source/src/vimeml/review/approve.py": {
372
+ "bytes": 3982,
373
+ "sha256": "b81ca9f1dcbe1b8e36f189723e81f2a3d44e9ef51f5a1c6f9ad0ce96a9b00536"
374
+ },
375
+ "source/src/vimeml/review/multi_key.py": {
376
+ "bytes": 8932,
377
+ "sha256": "e21ebd1e94159d17c26234191f2f1d1ee95e542c54a9762fe0a5717a73812e4a"
378
+ },
379
+ "source/src/vimeml/review/prepare.py": {
380
+ "bytes": 27338,
381
+ "sha256": "968209bb52d010c75066d2296a93bf27b30d264e614927d5e72bbe7c0a0b9323"
382
+ },
383
+ "source/src/vimeml/review/run.py": {
384
+ "bytes": 27866,
385
+ "sha256": "8507f1850e5b8af416108ff20b4283637d03fa267add33548674dedd7445b036"
386
+ },
387
+ "source/src/vimeml/tokenizer/__init__.py": {
388
+ "bytes": 35,
389
+ "sha256": "77c08158b861bafd5e902c886a0878a08a4afea25a5277dd33be60ae219ff624"
390
+ },
391
+ "source/src/vimeml/tokenizer/encode.py": {
392
+ "bytes": 9961,
393
+ "sha256": "92bdbdda6fa80f4dbb975f8882c021d8c7c0f9017f669c89dfae31dc2eecaa69"
394
+ },
395
+ "source/src/vimeml/tokenizer/encode_parallel.py": {
396
+ "bytes": 21649,
397
+ "sha256": "c194cb241d40b49669515f68f44278b55bcfbae210756f220b375cdee22ab009"
398
+ },
399
+ "source/src/vimeml/tokenizer/monitor.py": {
400
+ "bytes": 4352,
401
+ "sha256": "184a02f06f477a6037368ad2c2f43a32299fef83d009d851083e452ce91cf7d9"
402
+ },
403
+ "source/src/vimeml/tokenizer/native_train.py": {
404
+ "bytes": 387,
405
+ "sha256": "f42ef20a1e2b4aaa2bc6c04a2357b3c0375bb20a6055b49225318d08760ab01a"
406
+ },
407
+ "source/src/vimeml/tokenizer/store.py": {
408
+ "bytes": 3243,
409
+ "sha256": "53b285db5bfff0127281c8a35acc1beba8f5dfee1c6ae05e46923e91f9befd91"
410
+ },
411
+ "source/src/vimeml/tokenizer/train.py": {
412
+ "bytes": 21332,
413
+ "sha256": "81c218f4185345e443a5bfe1f80da024ad1260b6e3653ceb52938824cd60d752"
414
+ },
415
+ "source/src/vimeml/tools/__init__.py": {
416
+ "bytes": 31,
417
+ "sha256": "0a0511ce3c52639d6a3b0a1f030162d003bb930a2a7c795cc5f15fa68bdcb6b7"
418
+ },
419
+ "source/src/vimeml/tools/check_corpus.py": {
420
+ "bytes": 3242,
421
+ "sha256": "770f251e80a34626b3b36a193c9fd9ebaa3f4c78885382cef6cb060db7b3d762"
422
+ },
423
+ "source/src/vimeml/tools/inspect_data.py": {
424
+ "bytes": 4402,
425
+ "sha256": "5f180d035a6e73d3931959d1ae9a2eece8442390a59e522d0e908285205eb6c1"
426
+ },
427
+ "source/src/vimeml/tools/phrase_demo.html": {
428
+ "bytes": 9275,
429
+ "sha256": "c298c93b65c6c81782d5a4edea52d72e58d78a1db6c28412e15e338d97f409b5"
430
+ },
431
+ "source/src/vimeml/tools/phrase_demo.py": {
432
+ "bytes": 11017,
433
+ "sha256": "07023e63ece7f683038d0a823bac49722619940a0b1a387a989696effaaea16c"
434
+ },
435
+ "source/src/vimeml/training/__init__.py": {
436
+ "bytes": 64,
437
+ "sha256": "30be98c6d0dc0b68bb12915c37cb84d97cc3c47bc1c0734e45435ac033a02180"
438
+ },
439
+ "source/src/vimeml/training/check_data.py": {
440
+ "bytes": 7618,
441
+ "sha256": "c2530ba10df54c190211f59d1756f36de4792a4344dc78d14b88305e16a2f4be"
442
+ },
443
+ "source/src/vimeml/training/data.py": {
444
+ "bytes": 16536,
445
+ "sha256": "1627a4db9bb3c179977dc25e08b9443e84c0a966ff8416c3e5872c456b5b3ae3"
446
+ },
447
+ "source/src/vimeml/training/evaluate_ime.py": {
448
+ "bytes": 18317,
449
+ "sha256": "9a36b75893f58f61228ad7a6bbb2a34b52aaf312ca6675383580964500d639cd"
450
+ },
451
+ "source/src/vimeml/training/infer.py": {
452
+ "bytes": 8545,
453
+ "sha256": "7a093392ed15377c54bd1842cfbf32c6ab0257f3e956a81e84e41ad4624a9e9c"
454
+ },
455
+ "source/src/vimeml/training/model.py": {
456
+ "bytes": 4652,
457
+ "sha256": "dde6a0f8d7bd2d3a76e48fb503c22296da4c1156f976125bdb773d902bc7e07e"
458
+ },
459
+ "source/src/vimeml/training/train.py": {
460
+ "bytes": 25943,
461
+ "sha256": "dd34aa271f9a62af55aa877fc4090a1db0001cdce656faadd37304524eb59c5b"
462
+ },
463
+ "source/templates/huggingface/infer.py": {
464
+ "bytes": 1528,
465
+ "sha256": "8ed8d241d349a4e88840dd841da9db3191ec9f0db84c8f8165a951fa84720621"
466
+ },
467
+ "source/templates/huggingface/README.md": {
468
+ "bytes": 10512,
469
+ "sha256": "169add32b203da3142840133dc27456a63a576baecb5f9a9273d923464a1fcff"
470
+ },
471
+ "source/templates/huggingface/verify_release.py": {
472
+ "bytes": 2997,
473
+ "sha256": "80dd64ae4dac2eddbe864a2d2c4f429cf8b0bb6317b8eaf962fd378e11449b90"
474
+ },
475
+ "verify_release.py": {
476
+ "bytes": 2997,
477
+ "sha256": "80dd64ae4dac2eddbe864a2d2c4f429cf8b0bb6317b8eaf962fd378e11449b90"
478
+ }
479
+ },
480
+ "policy": "Local preparation only. No training, conversion, compression, device access or upload."
481
+ }
SHA256SUMS.txt ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 189b1af95d661151e054cea10c91b3d754e4de4d3fecfb074c1fb29476f7167b LICENSE
2
+ 366194211cc7db2f7de4f82471796b543f829e4c646a984758f2a5d589556f70 README.md
3
+ 9a1394b38c9f68911b1fa90ad06a371757fa069837bf9c24177c02761684fc24 RELEASE.json
4
+ a9d516233fa57aaadd09477e2e909760972ff49b5577919f8a441c489f5c2245 coreml/ios18-int8-block32/manifest.json
5
+ 22e80193abc7672bbcf2a3fc214a186ae6e64d76c1e1b42ead38fef5a003cd37 coreml/ios18-int8-block32/model.mlpackage/Data/com.apple.CoreML/model.mlmodel
6
+ d3a063455371e624cf208fb6ed76fc9ac6addd8c2f87eaebf4653649f85b8109 coreml/ios18-int8-block32/model.mlpackage/Data/com.apple.CoreML/weights/weight.bin
7
+ 31b13734848024036806cbd5bacf405eddba6c4451e5659b25f714ffe1766ee5 coreml/ios18-int8-block32/model.mlpackage/Manifest.json
8
+ d0fe405a4f68c3e5f94541834ab9f60cf0937b4f2fd6c50d25737e6ab828ad0e evaluation/summary.json
9
+ 8ed8d241d349a4e88840dd841da9db3191ec9f0db84c8f8165a951fa84720621 infer.py
10
+ 8950524f2f3a0373f3a0ebbcde79a8feaa54949ed926830c287b384306adf9ca inference/config.json
11
+ 4284cdc5491513cc817633081677b363b14f59c0b4ffb78cdf7fcfb221f71b3c inference/manifest.json
12
+ c7ebf3672d46cfc75d12013c34f35fb7ac9eac613f29f3454be0cb0e991854dc inference/token-manifest.json
13
+ d9f1ba1e456ce72dd9c06a62b12e804cf14090c30179e6078cd51d03063b2982 inference/tokenizer.model
14
+ 1cdac16913068bfdcfa94b139ecb34d945d0e9483a863373a6dca4397e3cd355 inference/weights.pt
15
+ 189b1af95d661151e054cea10c91b3d754e4de4d3fecfb074c1fb29476f7167b source/LICENSE
16
+ 5184082e7c857b4ab965d3c5b7366a0d5b56d98cdb5e40511dda6d0c2e298290 source/README.md
17
+ 67e3bf5c3166c9c5fa847ec515c44dd41dfebee56134265df2c5b13bec14ad9a source/configs/corpus-parallel.toml
18
+ 49f06564931dc4be63f76cdde8cd3be78ddeb81a45cbada768e0a0ca8191a49d source/configs/corpus-sharded.toml
19
+ 46d0b4eba6293a2fa2e7ef16d9132c6320e9d2da80921d60aa939e90e35dc222 source/configs/ime-eval-v1.json
20
+ 61c475fba63964b540e55be39d70e786886c059ce58fee94465517f648f021fe source/configs/ime-homophones.json
21
+ e701173b553617086ad2ab679313423eabb19e89d9f51fd31b321336584652ea source/configs/inference-prompts.json
22
+ e8baa76da3e223bcb3b333f96994ef4e86eeecb62cea18503eb8f7ad9c4a74cf source/configs/loader.toml
23
+ b51e101d6a4bc34c4a375439191e4adbd63d511ccc62b49c4d5b758decee17ed source/configs/phrase-demo-prompts.json
24
+ 28396838808e5745c4d2251e90e095d2a2e816033526bcb154d1c74c98dfe9b5 source/configs/tokenizer.toml
25
+ 2d386dd8efe0ffd0da6df319226daf7bab8948b1bcb2abc7afb59c45d98d28b3 source/configs/train-smoke.toml
26
+ 44ba93246d406d5187c496ad2cf23d1533e6f581a8d6dc6f7fff03fca22b8323 source/configs/train-v1.toml
27
+ 0fac5689cead9021745847b2753294a9a4d95c002a61762f7f25254db2a26489 source/requirements.txt
28
+ 34a2a24d14c0e8b20c44dafab720075cbc23261a7fdbe71091dff12200e7fc24 source/scripts/benchmarks/evaluate.py
29
+ a28fc24da1f8cbb6972b49dc0223361e16c7b093872d92df6bd52aabd00bd54a source/scripts/benchmarks/evaluate_ajimee.py
30
+ d328ff258e4a8fa83e51be099f5fa8fbf480f2d377579c475c355f9f01263bf6 source/scripts/benchmarks/export_cached_development.py
31
+ de9866c56cd846073a3a3a82b1553ab737ccb911001d17de71839865057d0293 source/scripts/benchmarks/generate.py
32
+ 3b3b4192535a035bbf57e3f51f1fb16a0647b1a3fc060a179b7ae9fb1127cc41 source/scripts/benchmarks/generate_development.py
33
+ 64dbd0fcee7e294bd290bbf9f2057e81656ede4e0b328cdae710360da24716b3 source/scripts/benchmarks/hybrid.py
34
+ 3935e16a81c389c5a3dd74da6da38732ff32b668ea3c6d0484c52f017657706a source/scripts/benchmarks/prepare_ajimee.py
35
+ 0e4d753adf6cf97cc19b975e4f35d3cea41a4f519782f6b8eeea3309260486df source/scripts/corpus/build.py
36
+ dd6639d55b9aecdbf46d20fec98ab78422ff53ad14ec5a4a39d6a43e824cd0d6 source/scripts/corpus/merge.py
37
+ f8181f1b055661a1097d2bf80654ac59af4cf6a8b74afecb8f44b40e3d94d4f8 source/scripts/corpus/preprocess.py
38
+ e3e12fd3373119539eb3ed23eaccbc1457cafa7a22ae444dd4b187c118d883ce source/scripts/deployment/ane_output_probe.py
39
+ 9a0761dbd3ece9b730ed0e52010ef965a8415b4320188c31e1863ab0c874d2fd source/scripts/deployment/compare_quantization.py
40
+ 2aa26fb2f2b59a630cc9cafb7eb684f237c44c780c1ec820badbbd07d8f2bf41 source/scripts/deployment/compute_plan.py
41
+ 7f321ab43b41acd4765045a93edc5c3ec874dcb741541a3f7460d13f8c27f50e source/scripts/deployment/coreml.py
42
+ 28c67b5b30c3ad93a46703dad62236b2cd81ec5478ebf2cd9974e7b336001871 source/scripts/deployment/coreml_ane.py
43
+ 61b290658385ee40325263be24b1a1dffdc18fe5bc494dd02a1bf93bf301b986 source/scripts/deployment/coreml_conservative.py
44
+ 65db41ee70efb5409738ece23eb41750e695f1746e42b85314198d72710fa7a7 source/scripts/deployment/package_handoff.py
45
+ 1228adeb409e3cf095aa7890390307b9cd518c535fc23365f5a8ffc01b144448 source/scripts/deployment/package_ios.py
46
+ 0857bab4b1dcf81ec8b1ae5e680672cf512d5547aa3246c1e2a7ad7bfc0b79e5 source/scripts/deployment/summarize_keyboard_trace.py
47
+ a798a4478ba907f0b4c88246c90d865063fa5c7c02f10456e7faceea7a7966ba source/scripts/deployment/summarize_memory.py
48
+ bbd7f36ed4682bcdcb5e4c5d87b42733106dbaebf6f949fcd60cd498dc059274 source/scripts/publishing/hub.py
49
+ 994c690a95f8d13d15a1ab1671185c8e2744537f48d667c176f9a3079b3630e9 source/scripts/publishing/prepare_hf.py
50
+ 555b275e74482c597a26b3cbc23ffb1648e636801bc5ddeccf7c25df176414d0 source/scripts/review/approve.py
51
+ 77578100f9aaae50cabac68a85996dcf02479179f6d17fbf30368ea7aa70ab5f source/scripts/review/prepare.py
52
+ d18a127f4a08b84ac03a398f3c96c935a0b6275fe0e7b1af2f3e3d4d92ec49b1 source/scripts/review/run.py
53
+ 702ba0084944c8db433f1d12f64e6933659965a9a125434cce80f916f63c6f47 source/scripts/review/run_multi_key.py
54
+ 46af106351c8835ba0e001295c4d2122c8c629d799c913feb8292d2e83100670 source/scripts/tokenizer/encode.py
55
+ 1001c67f565711d91c59335b342ab1a3e3fdb40f7f5642a8a49ce9ac46a4823d source/scripts/tokenizer/train.py
56
+ 231f867355c0cd2806e0ee20378c9671443309f2716db9dbf1426fda6bbe9eb6 source/scripts/tools/check_corpus.py
57
+ fd5a05f0c74e4d3bd95097a37be009422ec348c3fd3e36f1ca12740c02309760 source/scripts/tools/inspect_data.py
58
+ 09d81377f63d9c967e444ffdaaec83a7228de8109c4492a5175b46a8590387bc source/scripts/tools/phrase_demo.py
59
+ fce755bfd04595f95d6a021c5d2533599d3463542239da198d65e4c26a716e0a source/scripts/training/check_data.py
60
+ 81729525388462eb93ede97f917f0f6eae1ef6518c35d169e19dc745f37ffa4d source/scripts/training/evaluate_ime.py
61
+ 6f409f7761d543e65d5f4c8886c8f59e0929b4b299977a8439bd0d13623755f4 source/scripts/training/infer.py
62
+ 56b5781f98b3440d23d1516d830e83356db3a0b04c9aab134368c2ef9cefb91c source/scripts/training/train.py
63
+ db40a3f34315dcd409f0b22d8633c569f71f6807841fb2551a9ca423869ca149 source/scripts/training/train_wandb.py
64
+ ff0f42adee5b707f35ea0ba1307e65d8a2f632a55c3281c8f446211032f0fbc1 source/src/vimeml/__init__.py
65
+ 03a0b8a9e43ef3ecf56189a86fe7e33d9633d4a766edf02583e911aef9bde581 source/src/vimeml/benchmarks/__init__.py
66
+ 4916651168f3f883161ebbbc24db5e1ae5b1bc4cd6abf8c7b83560ef960406cb source/src/vimeml/benchmarks/ajimee.py
67
+ d74e8f9e888c115c6a9ce27f5b08ea40efb332c087d94e372d4c33364b2dc2d4 source/src/vimeml/benchmarks/development.py
68
+ 867cd2f41b49043dd985e7f05e788f3f83a0ac47a4d0d9b2d6306dcd0c96b02a source/src/vimeml/benchmarks/evaluate.py
69
+ 459a8c0d1bb4f8ffba57e92ee2c8bb3f5278c3e4a1a5bcc5dfda48a21bde3716 source/src/vimeml/benchmarks/evaluate_ajimee.py
70
+ 9a468c23ea3ebf02f1184b1742cbe11a025f2a0d7ae57e0ee2e54468de72a1d7 source/src/vimeml/benchmarks/generate.py
71
+ 71267f445ac92736f1dabf03c3e77193e9f2a1323f23fa1bc060aff4fbe4ea7e source/src/vimeml/benchmarks/hybrid.py
72
+ 55cdd3f899dd7ad156f8987cf2ba2bbbc76a95dfaba33a319058b4d01e2f9947 source/src/vimeml/corpus/__init__.py
73
+ 895b6894c4b85e72c8655cf6b42e21d51b51736d649ec2adb6841e033f4ac1f9 source/src/vimeml/corpus/build.py
74
+ 86d655d2d715fcb2f73269c1ffbfc9e87d9080a1baaf389f88e30de6022fdc19 source/src/vimeml/data/__init__.py
75
+ ba62919c8084d24cb3f41c451e970b5448e7b0f5407340c28cca0b220d387395 source/src/vimeml/data/annotations.py
76
+ d7d63e1ea9a2f9da9fe92ff2773b792c7ca89911b2d7058a2709fcbbc443081e source/src/vimeml/data/build.py
77
+ ee88adbb74d5a03f591231484928becf0a62557c85702efddd214c9bc8a2354f source/src/vimeml/data/clean.py
78
+ d2e943aae1e2942076b1db8430067e048fcdc71d46761bb329f7fa460e843280 source/src/vimeml/data/corpus_export.py
79
+ c196a78e5096e659eb8c536602e009c98de346bd82084c29f9384e526055f6f6 source/src/vimeml/data/corpus_parts.py
80
+ 43cb87845d17e52261675073b010269c53e4c79a8e5e16237d2627c9e07116e6 source/src/vimeml/data/merge_parts.py
81
+ 600dc56899dfa6ac2eda639cf79f6ad5e789642b99d6c68569262f593bfc3642 source/src/vimeml/data/merge_parts_fast.py
82
+ 433a93f18809aa1b11a13c8e63edce2a73864140fbaf4a4bcff0c7ab475be164 source/src/vimeml/data/preprocess_part.py
83
+ 837017b0282f62ed68c9a47c69da3ed12fe39b74e7f88bc3ae16113f55b679b6 source/src/vimeml/data/readers.py
84
+ 9834f4867e126f229e86a7fc5b7481f4c803c05d00a21966a78ec293095b0f8c source/src/vimeml/data/segment.py
85
+ 9301bfc92623e4a8b2589e01d7ca98aac917152544b83562b23628bf4cfe414f source/src/vimeml/deployment/__init__.py
86
+ 4f66f215e576ea1267971da0f49d995f49b09417920cbbf9e159e2d1c8083043 source/src/vimeml/deployment/bundle.py
87
+ 55d65451e04721c3c05a131b9c422f2e7c1835d6340cc8b76c4936243521dd6a source/src/vimeml/deployment/cli.py
88
+ 8281456e241e32ed79f9223d63f5c476e02b97b263723be9ad46c0a75435ee52 source/src/vimeml/deployment/coreml.py
89
+ e2474a97e4006c4e1fe14552b11874d08c52fa217b41c9d0ab0dbab7b0a79a67 source/src/vimeml/deployment/graph.py
90
+ 9e5d526dbd8e9be72f9d8782ac402623007226ee9e1ca14f8ce005a52a994c95 source/src/vimeml/deployment/validation.py
91
+ 6422f9e2042a114155c7caf2721c1f6bd8e5de4d153009703af1281f2a1c76c3 source/src/vimeml/review/__init__.py
92
+ b81ca9f1dcbe1b8e36f189723e81f2a3d44e9ef51f5a1c6f9ad0ce96a9b00536 source/src/vimeml/review/approve.py
93
+ e21ebd1e94159d17c26234191f2f1d1ee95e542c54a9762fe0a5717a73812e4a source/src/vimeml/review/multi_key.py
94
+ 968209bb52d010c75066d2296a93bf27b30d264e614927d5e72bbe7c0a0b9323 source/src/vimeml/review/prepare.py
95
+ 8507f1850e5b8af416108ff20b4283637d03fa267add33548674dedd7445b036 source/src/vimeml/review/run.py
96
+ 77c08158b861bafd5e902c886a0878a08a4afea25a5277dd33be60ae219ff624 source/src/vimeml/tokenizer/__init__.py
97
+ 92bdbdda6fa80f4dbb975f8882c021d8c7c0f9017f669c89dfae31dc2eecaa69 source/src/vimeml/tokenizer/encode.py
98
+ c194cb241d40b49669515f68f44278b55bcfbae210756f220b375cdee22ab009 source/src/vimeml/tokenizer/encode_parallel.py
99
+ 184a02f06f477a6037368ad2c2f43a32299fef83d009d851083e452ce91cf7d9 source/src/vimeml/tokenizer/monitor.py
100
+ f42ef20a1e2b4aaa2bc6c04a2357b3c0375bb20a6055b49225318d08760ab01a source/src/vimeml/tokenizer/native_train.py
101
+ 53b285db5bfff0127281c8a35acc1beba8f5dfee1c6ae05e46923e91f9befd91 source/src/vimeml/tokenizer/store.py
102
+ 81c218f4185345e443a5bfe1f80da024ad1260b6e3653ceb52938824cd60d752 source/src/vimeml/tokenizer/train.py
103
+ 0a0511ce3c52639d6a3b0a1f030162d003bb930a2a7c795cc5f15fa68bdcb6b7 source/src/vimeml/tools/__init__.py
104
+ 770f251e80a34626b3b36a193c9fd9ebaa3f4c78885382cef6cb060db7b3d762 source/src/vimeml/tools/check_corpus.py
105
+ 5f180d035a6e73d3931959d1ae9a2eece8442390a59e522d0e908285205eb6c1 source/src/vimeml/tools/inspect_data.py
106
+ c298c93b65c6c81782d5a4edea52d72e58d78a1db6c28412e15e338d97f409b5 source/src/vimeml/tools/phrase_demo.html
107
+ 07023e63ece7f683038d0a823bac49722619940a0b1a387a989696effaaea16c source/src/vimeml/tools/phrase_demo.py
108
+ 30be98c6d0dc0b68bb12915c37cb84d97cc3c47bc1c0734e45435ac033a02180 source/src/vimeml/training/__init__.py
109
+ c2530ba10df54c190211f59d1756f36de4792a4344dc78d14b88305e16a2f4be source/src/vimeml/training/check_data.py
110
+ 1627a4db9bb3c179977dc25e08b9443e84c0a966ff8416c3e5872c456b5b3ae3 source/src/vimeml/training/data.py
111
+ 9a36b75893f58f61228ad7a6bbb2a34b52aaf312ca6675383580964500d639cd source/src/vimeml/training/evaluate_ime.py
112
+ 7a093392ed15377c54bd1842cfbf32c6ab0257f3e956a81e84e41ad4624a9e9c source/src/vimeml/training/infer.py
113
+ dde6a0f8d7bd2d3a76e48fb503c22296da4c1156f976125bdb773d902bc7e07e source/src/vimeml/training/model.py
114
+ dd34aa271f9a62af55aa877fc4090a1db0001cdce656faadd37304524eb59c5b source/src/vimeml/training/train.py
115
+ 169add32b203da3142840133dc27456a63a576baecb5f9a9273d923464a1fcff source/templates/huggingface/README.md
116
+ 8ed8d241d349a4e88840dd841da9db3191ec9f0db84c8f8165a951fa84720621 source/templates/huggingface/infer.py
117
+ 80dd64ae4dac2eddbe864a2d2c4f429cf8b0bb6317b8eaf962fd378e11449b90 source/templates/huggingface/verify_release.py
118
+ 80dd64ae4dac2eddbe864a2d2c4f429cf8b0bb6317b8eaf962fd378e11449b90 verify_release.py
coreml/ios18-int8-block32/manifest.json ADDED
@@ -0,0 +1,491 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": "vimeml_coreml_v1",
3
+ "status": "complete",
4
+ "kind": "linear8_fp32_compute",
5
+ "minimum_ios": 18,
6
+ "bundle": {
7
+ "checkpoint_sha256": "93b6139aa05031df02038efcaf26788bc9916712919f3d2a4c4f398615cef75b",
8
+ "tokenizer_sha256": "d9f1ba1e456ce72dd9c06a62b12e804cf14090c30179e6078cd51d03063b2982",
9
+ "bundle_manifest_sha256": "4284cdc5491513cc817633081677b363b14f59c0b4ffb78cdf7fcfb221f71b3c",
10
+ "model": {
11
+ "vocab_size": 16384,
12
+ "context_length": 128,
13
+ "d_model": 256,
14
+ "n_heads": 4,
15
+ "n_layers": 4,
16
+ "d_ff": 1024,
17
+ "dropout": 0.0
18
+ },
19
+ "precision": "fp32",
20
+ "device": "cpu",
21
+ "environment": {
22
+ "python": "3.11.17",
23
+ "platform": "macOS-27.2-arm64-arm-64bit",
24
+ "machine": "arm64",
25
+ "packages": {
26
+ "torch": "2.7.1",
27
+ "numpy": "2.3.5",
28
+ "sentencepiece": "0.2.1",
29
+ "coremltools": "9.0"
30
+ }
31
+ }
32
+ },
33
+ "interface": {
34
+ "input_ids": "int32 [1,T], 1<=T<=128; right PAD only",
35
+ "logits": "float32 [1,T,16384]; FP32 computation; no softmax"
36
+ },
37
+ "source_manifest_sha256": "f5591ad1512b931a3a20765eb2218771f68e49699a3d23601e0cc0566d9c6d9d",
38
+ "fp16_alignment_sha256": "6bfead3f03f53f3e43780427515b3c5f12e6713845fd9f3a459df95aa23f800a",
39
+ "conversion_script_sha256": "4674f9d9e298562c8f517636674bf973532b8fc6ddc802aae8ca2aa939642912",
40
+ "precision_policy": {
41
+ "source_recipe": "fp16_weights_fp32_compute_preserve_positions_qkv_v1",
42
+ "computation": "FP32; token embedding gather is FP16 then cast to FP32"
43
+ },
44
+ "compression": {
45
+ "method": "linear",
46
+ "bits": 8,
47
+ "group_size": 16,
48
+ "block_size": 32,
49
+ "weight_threshold": 2048,
50
+ "selection": {
51
+ "policy": "linear_weights_and_named_embedding_tables_v1",
52
+ "selected_constants": [
53
+ {
54
+ "name": "model_position_embedding_weight",
55
+ "shape": [
56
+ 128,
57
+ 256
58
+ ],
59
+ "dtype": "float32",
60
+ "elements": 32768,
61
+ "consumers": [
62
+ {
63
+ "name": "op_143",
64
+ "type": "gather",
65
+ "inputs": {
66
+ "x": "model_position_embedding_weight"
67
+ }
68
+ }
69
+ ]
70
+ },
71
+ {
72
+ "name": "model_blocks_0_attention_qkv_weight",
73
+ "shape": [
74
+ 768,
75
+ 256
76
+ ],
77
+ "dtype": "float32",
78
+ "elements": 196608,
79
+ "consumers": [
80
+ {
81
+ "name": "linear_0",
82
+ "type": "linear",
83
+ "inputs": {
84
+ "weight": "model_blocks_0_attention_qkv_weight"
85
+ }
86
+ }
87
+ ]
88
+ },
89
+ {
90
+ "name": "model_blocks_1_attention_qkv_weight",
91
+ "shape": [
92
+ 768,
93
+ 256
94
+ ],
95
+ "dtype": "float32",
96
+ "elements": 196608,
97
+ "consumers": [
98
+ {
99
+ "name": "linear_4",
100
+ "type": "linear",
101
+ "inputs": {
102
+ "weight": "model_blocks_1_attention_qkv_weight"
103
+ }
104
+ }
105
+ ]
106
+ },
107
+ {
108
+ "name": "model_blocks_2_attention_qkv_weight",
109
+ "shape": [
110
+ 768,
111
+ 256
112
+ ],
113
+ "dtype": "float32",
114
+ "elements": 196608,
115
+ "consumers": [
116
+ {
117
+ "name": "linear_8",
118
+ "type": "linear",
119
+ "inputs": {
120
+ "weight": "model_blocks_2_attention_qkv_weight"
121
+ }
122
+ }
123
+ ]
124
+ },
125
+ {
126
+ "name": "model_blocks_3_attention_qkv_weight",
127
+ "shape": [
128
+ 768,
129
+ 256
130
+ ],
131
+ "dtype": "float32",
132
+ "elements": 196608,
133
+ "consumers": [
134
+ {
135
+ "name": "linear_12",
136
+ "type": "linear",
137
+ "inputs": {
138
+ "weight": "model_blocks_3_attention_qkv_weight"
139
+ }
140
+ }
141
+ ]
142
+ },
143
+ {
144
+ "name": "model_token_embedding_weight_storage_fp16",
145
+ "shape": [
146
+ 16384,
147
+ 256
148
+ ],
149
+ "dtype": "float16",
150
+ "elements": 4194304,
151
+ "consumers": [
152
+ {
153
+ "name": "op_139_storage_fp16",
154
+ "type": "gather",
155
+ "inputs": {
156
+ "x": "model_token_embedding_weight_storage_fp16"
157
+ }
158
+ },
159
+ {
160
+ "name": "linear_16_compute_fp32",
161
+ "type": "linear",
162
+ "inputs": {
163
+ "weight": "model_token_embedding_weight_storage_fp16",
164
+ "bias": "linear_16_bias_0_storage_fp16"
165
+ }
166
+ }
167
+ ]
168
+ },
169
+ {
170
+ "name": "model_blocks_0_attention_projection_weight_storage_fp16",
171
+ "shape": [
172
+ 256,
173
+ 256
174
+ ],
175
+ "dtype": "float16",
176
+ "elements": 65536,
177
+ "consumers": [
178
+ {
179
+ "name": "linear_1_compute_fp32",
180
+ "type": "linear",
181
+ "inputs": {
182
+ "weight": "model_blocks_0_attention_projection_weight_storage_fp16"
183
+ }
184
+ }
185
+ ]
186
+ },
187
+ {
188
+ "name": "model_blocks_0_ffn_0_weight_storage_fp16",
189
+ "shape": [
190
+ 1024,
191
+ 256
192
+ ],
193
+ "dtype": "float16",
194
+ "elements": 262144,
195
+ "consumers": [
196
+ {
197
+ "name": "linear_2_compute_fp32",
198
+ "type": "linear",
199
+ "inputs": {
200
+ "weight": "model_blocks_0_ffn_0_weight_storage_fp16"
201
+ }
202
+ }
203
+ ]
204
+ },
205
+ {
206
+ "name": "model_blocks_0_ffn_2_weight_storage_fp16",
207
+ "shape": [
208
+ 256,
209
+ 1024
210
+ ],
211
+ "dtype": "float16",
212
+ "elements": 262144,
213
+ "consumers": [
214
+ {
215
+ "name": "linear_3_compute_fp32",
216
+ "type": "linear",
217
+ "inputs": {
218
+ "weight": "model_blocks_0_ffn_2_weight_storage_fp16"
219
+ }
220
+ }
221
+ ]
222
+ },
223
+ {
224
+ "name": "model_blocks_1_attention_projection_weight_storage_fp16",
225
+ "shape": [
226
+ 256,
227
+ 256
228
+ ],
229
+ "dtype": "float16",
230
+ "elements": 65536,
231
+ "consumers": [
232
+ {
233
+ "name": "linear_5_compute_fp32",
234
+ "type": "linear",
235
+ "inputs": {
236
+ "weight": "model_blocks_1_attention_projection_weight_storage_fp16"
237
+ }
238
+ }
239
+ ]
240
+ },
241
+ {
242
+ "name": "model_blocks_1_ffn_0_weight_storage_fp16",
243
+ "shape": [
244
+ 1024,
245
+ 256
246
+ ],
247
+ "dtype": "float16",
248
+ "elements": 262144,
249
+ "consumers": [
250
+ {
251
+ "name": "linear_6_compute_fp32",
252
+ "type": "linear",
253
+ "inputs": {
254
+ "weight": "model_blocks_1_ffn_0_weight_storage_fp16"
255
+ }
256
+ }
257
+ ]
258
+ },
259
+ {
260
+ "name": "model_blocks_1_ffn_2_weight_storage_fp16",
261
+ "shape": [
262
+ 256,
263
+ 1024
264
+ ],
265
+ "dtype": "float16",
266
+ "elements": 262144,
267
+ "consumers": [
268
+ {
269
+ "name": "linear_7_compute_fp32",
270
+ "type": "linear",
271
+ "inputs": {
272
+ "weight": "model_blocks_1_ffn_2_weight_storage_fp16"
273
+ }
274
+ }
275
+ ]
276
+ },
277
+ {
278
+ "name": "model_blocks_2_attention_projection_weight_storage_fp16",
279
+ "shape": [
280
+ 256,
281
+ 256
282
+ ],
283
+ "dtype": "float16",
284
+ "elements": 65536,
285
+ "consumers": [
286
+ {
287
+ "name": "linear_9_compute_fp32",
288
+ "type": "linear",
289
+ "inputs": {
290
+ "weight": "model_blocks_2_attention_projection_weight_storage_fp16"
291
+ }
292
+ }
293
+ ]
294
+ },
295
+ {
296
+ "name": "model_blocks_2_ffn_0_weight_storage_fp16",
297
+ "shape": [
298
+ 1024,
299
+ 256
300
+ ],
301
+ "dtype": "float16",
302
+ "elements": 262144,
303
+ "consumers": [
304
+ {
305
+ "name": "linear_10_compute_fp32",
306
+ "type": "linear",
307
+ "inputs": {
308
+ "weight": "model_blocks_2_ffn_0_weight_storage_fp16"
309
+ }
310
+ }
311
+ ]
312
+ },
313
+ {
314
+ "name": "model_blocks_2_ffn_2_weight_storage_fp16",
315
+ "shape": [
316
+ 256,
317
+ 1024
318
+ ],
319
+ "dtype": "float16",
320
+ "elements": 262144,
321
+ "consumers": [
322
+ {
323
+ "name": "linear_11_compute_fp32",
324
+ "type": "linear",
325
+ "inputs": {
326
+ "weight": "model_blocks_2_ffn_2_weight_storage_fp16"
327
+ }
328
+ }
329
+ ]
330
+ },
331
+ {
332
+ "name": "model_blocks_3_attention_projection_weight_storage_fp16",
333
+ "shape": [
334
+ 256,
335
+ 256
336
+ ],
337
+ "dtype": "float16",
338
+ "elements": 65536,
339
+ "consumers": [
340
+ {
341
+ "name": "linear_13_compute_fp32",
342
+ "type": "linear",
343
+ "inputs": {
344
+ "weight": "model_blocks_3_attention_projection_weight_storage_fp16"
345
+ }
346
+ }
347
+ ]
348
+ },
349
+ {
350
+ "name": "model_blocks_3_ffn_0_weight_storage_fp16",
351
+ "shape": [
352
+ 1024,
353
+ 256
354
+ ],
355
+ "dtype": "float16",
356
+ "elements": 262144,
357
+ "consumers": [
358
+ {
359
+ "name": "linear_14_compute_fp32",
360
+ "type": "linear",
361
+ "inputs": {
362
+ "weight": "model_blocks_3_ffn_0_weight_storage_fp16"
363
+ }
364
+ }
365
+ ]
366
+ },
367
+ {
368
+ "name": "model_blocks_3_ffn_2_weight_storage_fp16",
369
+ "shape": [
370
+ 256,
371
+ 1024
372
+ ],
373
+ "dtype": "float16",
374
+ "elements": 262144,
375
+ "consumers": [
376
+ {
377
+ "name": "linear_15_compute_fp32",
378
+ "type": "linear",
379
+ "inputs": {
380
+ "weight": "model_blocks_3_ffn_2_weight_storage_fp16"
381
+ }
382
+ }
383
+ ]
384
+ }
385
+ ],
386
+ "excluded_constants": [
387
+ {
388
+ "name": "causal_mask",
389
+ "shape": [
390
+ 128,
391
+ 128
392
+ ],
393
+ "dtype": "float32",
394
+ "elements": 16384,
395
+ "consumers": [
396
+ {
397
+ "name": "op_182",
398
+ "type": "slice_by_index",
399
+ "inputs": {
400
+ "x": "causal_mask"
401
+ }
402
+ }
403
+ ],
404
+ "reason": "Not a linear weight or named embedding parameter",
405
+ "nonfinite_elements": 8128
406
+ },
407
+ {
408
+ "name": "linear_16_bias_0_storage_fp16",
409
+ "shape": [
410
+ 16384
411
+ ],
412
+ "dtype": "float16",
413
+ "elements": 16384,
414
+ "consumers": [
415
+ {
416
+ "name": "linear_16_compute_fp32",
417
+ "type": "linear",
418
+ "inputs": {
419
+ "weight": "model_token_embedding_weight_storage_fp16",
420
+ "bias": "linear_16_bias_0_storage_fp16"
421
+ }
422
+ }
423
+ ],
424
+ "reason": "Not a linear weight or named embedding parameter",
425
+ "nonfinite_elements": 0
426
+ }
427
+ ],
428
+ "selected_elements": 7372800
429
+ },
430
+ "scope": "Eligible learned matrices, including FP32 QKV; structural constants/masks unchanged; no activation quantization or retraining."
431
+ },
432
+ "environment": {
433
+ "python": "3.11.17",
434
+ "platform": "macOS-27.2-arm64-arm-64bit",
435
+ "machine": "arm64",
436
+ "packages": {
437
+ "torch": "2.7.1",
438
+ "numpy": "2.3.5",
439
+ "sentencepiece": "0.2.1",
440
+ "coremltools": "9.0"
441
+ }
442
+ },
443
+ "spec": {
444
+ "specification_version": 9,
445
+ "operations": {
446
+ "const": 153,
447
+ "constexpr_blockwise_shift_scale": 18,
448
+ "shape": 1,
449
+ "gather": 3,
450
+ "range_1d": 1,
451
+ "greater_equal": 6,
452
+ "add": 19,
453
+ "select": 6,
454
+ "cast": 1,
455
+ "layer_norm": 9,
456
+ "linear": 17,
457
+ "reshape": 8,
458
+ "transpose": 8,
459
+ "split": 4,
460
+ "squeeze": 12,
461
+ "matmul": 8,
462
+ "mul": 4,
463
+ "concat": 2,
464
+ "slice_by_index": 2,
465
+ "softmax": 4,
466
+ "gelu": 4
467
+ },
468
+ "inputs": [
469
+ "input_ids"
470
+ ],
471
+ "outputs": [
472
+ "logits"
473
+ ]
474
+ },
475
+ "package_files": {
476
+ "Data/com.apple.CoreML/model.mlmodel": {
477
+ "bytes": 46400,
478
+ "sha256": "22e80193abc7672bbcf2a3fc214a186ae6e64d76c1e1b42ead38fef5a003cd37"
479
+ },
480
+ "Data/com.apple.CoreML/weights/weight.bin": {
481
+ "bytes": 8030784,
482
+ "sha256": "d3a063455371e624cf208fb6ed76fc9ac6addd8c2f87eaebf4653649f85b8109"
483
+ },
484
+ "Manifest.json": {
485
+ "bytes": 617,
486
+ "sha256": "31b13734848024036806cbd5bacf405eddba6c4451e5659b25f714ffe1766ee5"
487
+ }
488
+ },
489
+ "package_bytes": 8077801,
490
+ "note": "Logical file bytes, not compiled size, IPA size or resident memory. Inspect weight.bin and const/constexpr ops for tied-weight duplication."
491
+ }
coreml/ios18-int8-block32/model.mlpackage/Data/com.apple.CoreML/model.mlmodel ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:22e80193abc7672bbcf2a3fc214a186ae6e64d76c1e1b42ead38fef5a003cd37
3
+ size 46400
coreml/ios18-int8-block32/model.mlpackage/Data/com.apple.CoreML/weights/weight.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d3a063455371e624cf208fb6ed76fc9ac6addd8c2f87eaebf4653649f85b8109
3
+ size 8030784
coreml/ios18-int8-block32/model.mlpackage/Manifest.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "fileFormatVersion": "1.0.0",
3
+ "itemInfoEntries": {
4
+ "B5BC0804-8142-444E-8A75-43E553098EA8": {
5
+ "author": "com.apple.CoreML",
6
+ "description": "CoreML Model Weights",
7
+ "name": "weights",
8
+ "path": "com.apple.CoreML/weights"
9
+ },
10
+ "C4D1258E-24AB-4839-B8C6-61581D74BA25": {
11
+ "author": "com.apple.CoreML",
12
+ "description": "CoreML Model Specification",
13
+ "name": "model.mlmodel",
14
+ "path": "com.apple.CoreML/model.mlmodel"
15
+ }
16
+ },
17
+ "rootModelIdentifier": "C4D1258E-24AB-4839-B8C6-61581D74BA25"
18
+ }
evaluation/summary.json ADDED
@@ -0,0 +1,239 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": "vimeml_public_evaluation_v1",
3
+ "bundle_manifest_sha256": "4284cdc5491513cc817633081677b363b14f59c0b4ffb78cdf7fcfb221f71b3c",
4
+ "coreml_manifest_sha256": "a9d516233fa57aaadd09477e2e909760972ff49b5577919f8a441c489f5c2245",
5
+ "training": {
6
+ "prediction_targets": 573295237,
7
+ "data_passes": 1.0,
8
+ "full_validation": {
9
+ "loss": 4.541570895102848,
10
+ "prediction_pairs": 5890326,
11
+ "perplexity": 93.83809420004854
12
+ },
13
+ "test_used_for_selection": false
14
+ },
15
+ "ranking": {
16
+ "dev": {
17
+ "original_order": {
18
+ "cases": 137,
19
+ "top1_correct": 111,
20
+ "top1_accuracy": 0.8102189781021898,
21
+ "top5_correct": 130,
22
+ "top5_accuracy": 0.948905109489051,
23
+ "candidate_pool_covered": 136,
24
+ "candidate_pool_recall": 0.9927007299270073,
25
+ "covered_top1_accuracy": 0.8161764705882353,
26
+ "mean_min_cer": 0.04746998743349109,
27
+ "mrr": 0.8745925911071898,
28
+ "corrected_vs_azookey": 0,
29
+ "regressed_vs_azookey": 0,
30
+ "fallback_cases": 0
31
+ },
32
+ "fp32_lm": {
33
+ "cases": 137,
34
+ "top1_correct": 122,
35
+ "top1_accuracy": 0.8905109489051095,
36
+ "top5_correct": 134,
37
+ "top5_accuracy": 0.9781021897810219,
38
+ "candidate_pool_covered": 136,
39
+ "candidate_pool_recall": 0.9927007299270073,
40
+ "covered_top1_accuracy": 0.8970588235294118,
41
+ "mean_min_cer": 0.03045798483754688,
42
+ "mrr": 0.935349322210636,
43
+ "corrected_vs_azookey": 15,
44
+ "regressed_vs_azookey": 4,
45
+ "fallback_cases": 0
46
+ },
47
+ "int8_lm": {
48
+ "cases": 137,
49
+ "top1_correct": 122,
50
+ "top1_accuracy": 0.8905109489051095,
51
+ "top5_correct": 134,
52
+ "top5_accuracy": 0.9781021897810219,
53
+ "candidate_pool_covered": 136,
54
+ "candidate_pool_recall": 0.9927007299270073,
55
+ "covered_top1_accuracy": 0.8970588235294118,
56
+ "mean_min_cer": 0.03045798483754688,
57
+ "mrr": 0.935349322210636,
58
+ "corrected_vs_azookey": 15,
59
+ "regressed_vs_azookey": 4,
60
+ "fallback_cases": 0
61
+ },
62
+ "windows_fp32_comparison": {
63
+ "order_changed_cases": 119,
64
+ "top1_changed_cases": 0,
65
+ "max_score_sum_abs": 1.9853296279907227,
66
+ "mean_score_sum_abs": 0.20199488408841776
67
+ },
68
+ "original_report_comparison_baseline": "conservative Core ML",
69
+ "original_report_comparison": {
70
+ "order_changed_cases": 118,
71
+ "top1_changed_cases": 0,
72
+ "max_score_sum_abs": 1.994759202003479,
73
+ "mean_score_sum_abs": 0.2013680215637894
74
+ },
75
+ "benchmark_source_sha256": "a945f2117570530ef73b1dd4ef53b9dc69b07fa69ac98a6d67c9268b540eafa8",
76
+ "candidate_export_sha256": "ab192509dce1cb4f4b20273e623f7bfd4237b1c3691950438e6921b8a510ae21"
77
+ },
78
+ "ajimee": {
79
+ "original_order": {
80
+ "cases": 200,
81
+ "top1_correct": 87,
82
+ "top1_accuracy": 0.435,
83
+ "top5_correct": 143,
84
+ "top5_accuracy": 0.715,
85
+ "candidate_pool_covered": 162,
86
+ "candidate_pool_recall": 0.81,
87
+ "covered_top1_accuracy": 0.5370370370370371,
88
+ "mean_min_cer": 0.0821630611981915,
89
+ "mrr": 0.554232017982018,
90
+ "corrected_vs_azookey": 0,
91
+ "regressed_vs_azookey": 0,
92
+ "fallback_cases": 0
93
+ },
94
+ "fp32_lm": {
95
+ "cases": 200,
96
+ "top1_correct": 124,
97
+ "top1_accuracy": 0.62,
98
+ "top5_correct": 151,
99
+ "top5_accuracy": 0.755,
100
+ "candidate_pool_covered": 162,
101
+ "candidate_pool_recall": 0.81,
102
+ "covered_top1_accuracy": 0.7654320987654321,
103
+ "mean_min_cer": 0.05657040945427151,
104
+ "mrr": 0.684061974789916,
105
+ "corrected_vs_azookey": 48,
106
+ "regressed_vs_azookey": 11,
107
+ "fallback_cases": 0
108
+ },
109
+ "int8_lm": {
110
+ "cases": 200,
111
+ "top1_correct": 124,
112
+ "top1_accuracy": 0.62,
113
+ "top5_correct": 151,
114
+ "top5_accuracy": 0.755,
115
+ "candidate_pool_covered": 162,
116
+ "candidate_pool_recall": 0.81,
117
+ "covered_top1_accuracy": 0.7654320987654321,
118
+ "mean_min_cer": 0.05657040945427151,
119
+ "mrr": 0.6841512605042016,
120
+ "corrected_vs_azookey": 48,
121
+ "regressed_vs_azookey": 11,
122
+ "fallback_cases": 0
123
+ },
124
+ "windows_fp32_comparison": {
125
+ "order_changed_cases": 163,
126
+ "top1_changed_cases": 2,
127
+ "max_score_sum_abs": 2.6928324103355408,
128
+ "mean_score_sum_abs": 0.33380034679328247
129
+ },
130
+ "original_report_comparison_baseline": "Windows FP32",
131
+ "original_report_comparison": {
132
+ "order_changed_cases": 163,
133
+ "top1_changed_cases": 2,
134
+ "max_score_sum_abs": 2.6928324103355408,
135
+ "mean_score_sum_abs": 0.33380034679328247
136
+ },
137
+ "benchmark_source_sha256": "e9eb668fd6aa14b1e26436f429b5550108af0a1dfd443b8cea0bcb3ab3028fca",
138
+ "candidate_export_sha256": "6b532af2e7b1536ded98e9d75cbba4fd7338b4204987fc4dafb16df7edfdf223"
139
+ }
140
+ },
141
+ "alignment": {
142
+ "strict_passed": false,
143
+ "max_logit_abs": 3.6601905822753906,
144
+ "invariants": {
145
+ "right_pad": {
146
+ "max_abs": 2.8133392333984375e-05,
147
+ "mean_abs": 2.5544544911072975e-06,
148
+ "rmse": 3.420821628891477e-06,
149
+ "outside_tolerance": 0,
150
+ "values": 114688
151
+ },
152
+ "causal": {
153
+ "max_abs": 0.0,
154
+ "mean_abs": 0.0,
155
+ "rmse": 0.0,
156
+ "outside_tolerance": 0,
157
+ "values": 262144
158
+ }
159
+ }
160
+ },
161
+ "distribution": {
162
+ "original_report": {
163
+ "model": "artifacts/deployment/tiny-ja-v1-conservative-int8-b32-v1",
164
+ "compute_units": "CPU_ONLY",
165
+ "benchmark_text": {
166
+ "kl_coreml": {
167
+ "mean": 0.005891128288349137,
168
+ "max": 0.025890197604894638
169
+ },
170
+ "kl_simulated": {
171
+ "mean": 0.005977873008232564,
172
+ "max": 0.028363749384880066
173
+ },
174
+ "nll_fp32": {
175
+ "mean": 5.566977231502533,
176
+ "max": 8.531479835510254
177
+ },
178
+ "nll_coreml": {
179
+ "mean": 5.572893209457398,
180
+ "max": 8.633211135864258
181
+ },
182
+ "top1_agreement": {
183
+ "mean": 0.943656535744667,
184
+ "max": 1.0
185
+ },
186
+ "max_logprob_diff": {
187
+ "mean": 0.9077084493637085,
188
+ "max": 3.5512266159057617
189
+ }
190
+ },
191
+ "random_tokens": {
192
+ "kl_coreml": {
193
+ "mean": 0.015501508116722107,
194
+ "max": 0.01807662844657898
195
+ },
196
+ "kl_simulated": {
197
+ "mean": 0.015804098173975944,
198
+ "max": 0.01901794597506523
199
+ },
200
+ "nll_fp32": {
201
+ "mean": 11.800455474853516,
202
+ "max": 12.100545883178711
203
+ },
204
+ "nll_coreml": {
205
+ "mean": 11.795885848999024,
206
+ "max": 12.123295783996582
207
+ },
208
+ "top1_agreement": {
209
+ "mean": 0.9140625,
210
+ "max": 0.9296875
211
+ },
212
+ "max_logprob_diff": {
213
+ "mean": 2.206966495513916,
214
+ "max": 2.564694404602051
215
+ }
216
+ }
217
+ },
218
+ "aggregation": "Mean of per-example position means; not corpus token-weighted.",
219
+ "content_token_limit": 127,
220
+ "truncation_count_recorded": false,
221
+ "original_report_has_full_identity_metadata": false
222
+ },
223
+ "source_reports_sha256": {
224
+ "outputs/deployment/conservative-int8-b32-dev-v1/metrics.json": "839e220c3024ec9798c921fb39d8862c5f874889c9dd9c2066cb339894658329",
225
+ "outputs/deployment/conservative-int8-b32-dev-v1/scores.jsonl": "f00a6b56b96e6be1db3ba7c58abe53bc91e7614c2f7f7959c27b0a8cce8db936",
226
+ "outputs/ime-eval/tiny-ja-v1-dev-v2-scores/manifest.json": "16bbd3b5b921e54896cfdca880d77c78fedd76b2aa8956fbaaa4c45bd4a4030e",
227
+ "outputs/ime-eval/tiny-ja-v1-dev-v2-scores/scores.jsonl": "2710b88428b5f73c6aeac5d45d442fa0d9bca1e038db2928c6059be993f3cfcb",
228
+ "outputs/deployment/conservative-dev-v3/metrics.json": "387d80e1b52a2c8dbcbb2196471ae548ae869b1e3f259dc1ee972c592e6b4863",
229
+ "outputs/deployment/conservative-dev-v3/scores.jsonl": "bb4fa9f52a074144a3cb432777ec6c94ad07637cdecd5b2126ad5d64a4e6a1d2",
230
+ "outputs/deployment/conservative-int8-b32-ajimee-v1/metrics.json": "3b59e396173328c78e8164f809da24764474685a2bd35765457fac5df644aac5",
231
+ "outputs/deployment/conservative-int8-b32-ajimee-v1/scores.jsonl": "9daf557818ab0aef12fb74e68b8cd4ae502806650d3d7e0cd23eeb2e1499e89e",
232
+ "outputs/ime-eval/tiny-ja-v1-ajimee/metrics.json": "219e15ec8ee54b09b455bd14ce60ab99bff239510ab9df344b69d2afc4b21196",
233
+ "outputs/ime-eval/tiny-ja-v1-ajimee/scores.jsonl": "247b29b59285551acaf73fabf9bee79c9617360623329e6bb60deb2d33ee7ddf",
234
+ "outputs/deployment/conservative-int8-b32-alignment-v1/alignment.json": "6773fafbd84e27dc1c349234fe601898e9f2af56ec5e2dcc6a32b12fbe14d018",
235
+ "outputs/deployment/conservative-int8-b32-distribution-v1/report.json": "6970eafdbb0d8e8b075d5b38b241cfa8fa46777961106ac32714755950722852",
236
+ "artifacts/models/tiny-ja-v1/summary.json": "4d9417b3bb8702b025b1ce4962d38ea36ad6d52928d19267dccdcaed3bf6c022"
237
+ },
238
+ "scope": "Offline fixed pools and saved Mac observations; not app-wide accuracy or a Windows Core ML runtime rerun."
239
+ }
infer.py ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Read-only FP32 inference using the included, fingerprint-checked VimeML source."""
2
+ import argparse
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ ROOT = Path(__file__).resolve().parent
8
+
9
+
10
+ def main():
11
+ parser = argparse.ArgumentParser(description=__doc__)
12
+ parser.add_argument('--prompt', default='今日は雨が降っているので、')
13
+ parser.add_argument('--candidate', action='append', help='Rerank these strings after --prompt; repeat for each candidate.')
14
+ parser.add_argument('--max-new-tokens', type=int, default=8)
15
+ parser.add_argument('--threads', type=int, default=4)
16
+ args = parser.parse_args()
17
+ if args.threads < 1 or args.max_new_tokens < 1:
18
+ parser.error('threads and max-new-tokens must be positive.')
19
+ # The wrapper has no model math or scoring changes. Original code lives in source/.
20
+ sys.path.insert(0, str(ROOT / 'source/src'))
21
+ import torch
22
+ from vimeml.deployment.bundle import BundleLM
23
+ from vimeml.training.evaluate_ime import score_candidates
24
+ torch.set_num_threads(args.threads)
25
+ lm = BundleLM(ROOT / 'inference')
26
+ if args.candidate:
27
+ result = score_candidates(lm, args.prompt, args.candidate)
28
+ result['ranked_candidates'] = sorted(result['candidates'],
29
+ key=lambda item: item['log_probability_sum'], reverse=True)
30
+ else:
31
+ result = lm.generate(args.prompt, max_new_tokens=args.max_new_tokens, temperature=0)
32
+ print(json.dumps(result, ensure_ascii=False, indent=2))
33
+
34
+
35
+ if __name__ == '__main__':
36
+ main()
inference/config.json ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "vocab_size": 16384,
3
+ "context_length": 128,
4
+ "d_model": 256,
5
+ "n_heads": 4,
6
+ "n_layers": 4,
7
+ "d_ff": 1024,
8
+ "dropout": 0.0
9
+ }
inference/manifest.json ADDED
@@ -0,0 +1,108 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": "vimeml_inference_bundle_v1",
3
+ "status": "complete",
4
+ "parameter_count": 7386624,
5
+ "precision": "fp32",
6
+ "special_ids": {
7
+ "pad": 0,
8
+ "unk": 1,
9
+ "bos": 2,
10
+ "eos": 3
11
+ },
12
+ "tied_lm_head": true,
13
+ "checkpoint_sha256": "93b6139aa05031df02038efcaf26788bc9916712919f3d2a4c4f398615cef75b",
14
+ "checkpoint_step": 196853,
15
+ "training_signatures": {
16
+ "tokens": "c7ebf3672d46cfc75d12013c34f35fb7ac9eac613f29f3454be0cb0e991854dc",
17
+ "windows": "4cdc90177a3fac335ad39434156f54ad20aa143190700950a122d914fd05d617",
18
+ "training_code": {
19
+ "train.py": "dd34aa271f9a62af55aa877fc4090a1db0001cdce656faadd37304524eb59c5b",
20
+ "model.py": "dde6a0f8d7bd2d3a76e48fb503c22296da4c1156f976125bdb773d902bc7e07e",
21
+ "data.py": "1627a4db9bb3c179977dc25e08b9443e84c0a966ff8416c3e5872c456b5b3ae3"
22
+ }
23
+ },
24
+ "source_fingerprints": {
25
+ "src/vimeml/training/model.py": {
26
+ "sha256": "dde6a0f8d7bd2d3a76e48fb503c22296da4c1156f976125bdb773d902bc7e07e",
27
+ "lf_sha256": "dde6a0f8d7bd2d3a76e48fb503c22296da4c1156f976125bdb773d902bc7e07e"
28
+ },
29
+ "src/vimeml/training/data.py": {
30
+ "sha256": "1627a4db9bb3c179977dc25e08b9443e84c0a966ff8416c3e5872c456b5b3ae3",
31
+ "lf_sha256": "1627a4db9bb3c179977dc25e08b9443e84c0a966ff8416c3e5872c456b5b3ae3"
32
+ },
33
+ "src/vimeml/training/train.py": {
34
+ "sha256": "dd34aa271f9a62af55aa877fc4090a1db0001cdce656faadd37304524eb59c5b",
35
+ "lf_sha256": "dd34aa271f9a62af55aa877fc4090a1db0001cdce656faadd37304524eb59c5b"
36
+ },
37
+ "src/vimeml/training/infer.py": {
38
+ "sha256": "7a093392ed15377c54bd1842cfbf32c6ab0257f3e956a81e84e41ad4624a9e9c",
39
+ "lf_sha256": "7a093392ed15377c54bd1842cfbf32c6ab0257f3e956a81e84e41ad4624a9e9c"
40
+ },
41
+ "src/vimeml/training/evaluate_ime.py": {
42
+ "sha256": "9a36b75893f58f61228ad7a6bbb2a34b52aaf312ca6675383580964500d639cd",
43
+ "lf_sha256": "9a36b75893f58f61228ad7a6bbb2a34b52aaf312ca6675383580964500d639cd"
44
+ },
45
+ "src/vimeml/benchmarks/evaluate_ajimee.py": {
46
+ "sha256": "459a8c0d1bb4f8ffba57e92ee2c8bb3f5278c3e4a1a5bcc5dfda48a21bde3716",
47
+ "lf_sha256": "459a8c0d1bb4f8ffba57e92ee2c8bb3f5278c3e4a1a5bcc5dfda48a21bde3716"
48
+ },
49
+ "src/vimeml/tools/phrase_demo.py": {
50
+ "sha256": "07023e63ece7f683038d0a823bac49722619940a0b1a387a989696effaaea16c",
51
+ "lf_sha256": "07023e63ece7f683038d0a823bac49722619940a0b1a387a989696effaaea16c"
52
+ },
53
+ "src/vimeml/deployment/__init__.py": {
54
+ "sha256": "9301bfc92623e4a8b2589e01d7ca98aac917152544b83562b23628bf4cfe414f",
55
+ "lf_sha256": "9301bfc92623e4a8b2589e01d7ca98aac917152544b83562b23628bf4cfe414f"
56
+ },
57
+ "src/vimeml/deployment/bundle.py": {
58
+ "sha256": "4f66f215e576ea1267971da0f49d995f49b09417920cbbf9e159e2d1c8083043",
59
+ "lf_sha256": "4f66f215e576ea1267971da0f49d995f49b09417920cbbf9e159e2d1c8083043"
60
+ },
61
+ "src/vimeml/deployment/cli.py": {
62
+ "sha256": "55d65451e04721c3c05a131b9c422f2e7c1835d6340cc8b76c4936243521dd6a",
63
+ "lf_sha256": "55d65451e04721c3c05a131b9c422f2e7c1835d6340cc8b76c4936243521dd6a"
64
+ },
65
+ "src/vimeml/deployment/coreml.py": {
66
+ "sha256": "8281456e241e32ed79f9223d63f5c476e02b97b263723be9ad46c0a75435ee52",
67
+ "lf_sha256": "8281456e241e32ed79f9223d63f5c476e02b97b263723be9ad46c0a75435ee52"
68
+ },
69
+ "src/vimeml/deployment/graph.py": {
70
+ "sha256": "e2474a97e4006c4e1fe14552b11874d08c52fa217b41c9d0ab0dbab7b0a79a67",
71
+ "lf_sha256": "e2474a97e4006c4e1fe14552b11874d08c52fa217b41c9d0ab0dbab7b0a79a67"
72
+ },
73
+ "src/vimeml/deployment/validation.py": {
74
+ "sha256": "9e5d526dbd8e9be72f9d8782ac402623007226ee9e1ca14f8ce005a52a994c95",
75
+ "lf_sha256": "9e5d526dbd8e9be72f9d8782ac402623007226ee9e1ca14f8ce005a52a994c95"
76
+ }
77
+ },
78
+ "environment": {
79
+ "python": "3.13.5",
80
+ "platform": "Windows-11-10.0.26300-SP0",
81
+ "machine": "AMD64",
82
+ "packages": {
83
+ "torch": "2.10.0+cu128",
84
+ "numpy": "2.5.3",
85
+ "sentencepiece": "0.2.1",
86
+ "coremltools": null
87
+ }
88
+ },
89
+ "files": {
90
+ "config.json": {
91
+ "bytes": 145,
92
+ "sha256": "8950524f2f3a0373f3a0ebbcde79a8feaa54949ed926830c287b384306adf9ca"
93
+ },
94
+ "token-manifest.json": {
95
+ "bytes": 6208,
96
+ "sha256": "c7ebf3672d46cfc75d12013c34f35fb7ac9eac613f29f3454be0cb0e991854dc"
97
+ },
98
+ "tokenizer.model": {
99
+ "bytes": 284118,
100
+ "sha256": "d9f1ba1e456ce72dd9c06a62b12e804cf14090c30179e6078cd51d03063b2982"
101
+ },
102
+ "weights.pt": {
103
+ "bytes": 29562119,
104
+ "sha256": "1cdac16913068bfdcfa94b139ecb34d945d0e9483a863373a6dca4397e3cd355"
105
+ }
106
+ },
107
+ "policy": "Weights only; no optimizer, RNG or training state. LM-only contextual suffix logP sum; no extra EOS; no KV cache."
108
+ }
inference/token-manifest.json ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "status": "complete",
3
+ "format": "vimeml_sentence_tokens_v1",
4
+ "vocab_size": 16384,
5
+ "token_dtype": "uint16_le",
6
+ "offset_dtype": "uint64_le",
7
+ "offset_unit": "tokens",
8
+ "special_ids": {
9
+ "pad": 0,
10
+ "unk": 1,
11
+ "bos": 2,
12
+ "eos": 3
13
+ },
14
+ "sentencepiece_version": "0.2.1",
15
+ "splits": {
16
+ "train": {
17
+ "sentences": 25185368,
18
+ "stored_tokens": 598480605,
19
+ "content_tokens": 548109869,
20
+ "characters": 1080952698,
21
+ "prediction_pairs": 573295237,
22
+ "max_sequence_tokens": 590,
23
+ "primary_sources": {
24
+ "fineweb": 24938228,
25
+ "tatoeba": 247140
26
+ },
27
+ "file_bytes": {
28
+ "tokens.bin": 1196961210,
29
+ "offsets.bin": 201482952,
30
+ "rows.bin": 201482944,
31
+ "sources.bin": 25185368
32
+ }
33
+ },
34
+ "validation": {
35
+ "sentences": 260337,
36
+ "stored_tokens": 6150663,
37
+ "content_tokens": 5629989,
38
+ "characters": 11110489,
39
+ "prediction_pairs": 5890326,
40
+ "max_sequence_tokens": 439,
41
+ "primary_sources": {
42
+ "fineweb": 257916,
43
+ "tatoeba": 2421
44
+ },
45
+ "file_bytes": {
46
+ "tokens.bin": 12301326,
47
+ "offsets.bin": 2082704,
48
+ "rows.bin": 2082696,
49
+ "sources.bin": 260337
50
+ }
51
+ },
52
+ "test": {
53
+ "sentences": 267298,
54
+ "stored_tokens": 6258211,
55
+ "content_tokens": 5723615,
56
+ "characters": 11296824,
57
+ "prediction_pairs": 5990913,
58
+ "max_sequence_tokens": 497,
59
+ "primary_sources": {
60
+ "fineweb": 264698,
61
+ "tatoeba": 2600
62
+ },
63
+ "file_bytes": {
64
+ "tokens.bin": 12516422,
65
+ "offsets.bin": 2138392,
66
+ "rows.bin": 2138384,
67
+ "sources.bin": 267298
68
+ }
69
+ }
70
+ },
71
+ "source_ids": {
72
+ "fineweb": 0,
73
+ "tatoeba": 1
74
+ },
75
+ "sequence_policy": "Each sequence is BOS + complete encoded sentence + EOS; no truncation or concatenation.",
76
+ "prediction_policy": "x=s[:-1], y=s[1:]; sentence boundaries remain separate.",
77
+ "provenance_policy": "Index equals zero-based corpus JSONL/TXT row. rows.bin stores uint64 little-endian byte positions in the source JSONL; sources.bin stores uint8 primary source IDs. Full original metadata stays in corpus JSONL.",
78
+ "corpus_dir": "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1",
79
+ "tokenizer_dir": "D:\\Sources\\VimeML\\artifacts\\tokenizers\\ja-unigram-16k-v1",
80
+ "input_sha256": {
81
+ "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1\\train.txt": "9c1b4585adaf0518dc0275e7f8086e967db26f9797fb571e1ff481aeadf2ff8f",
82
+ "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1\\train.jsonl": "0c8bb3db59cd820db6315c63cd1b7adc92e7afcb7e8bc82ee39820f1e3d7ea21",
83
+ "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1\\validation.txt": "f188bea10a8b464dd385d8e0aa310225de07e2a0a170e90ce63503c150bf4cdd",
84
+ "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1\\validation.jsonl": "544773d7bd40ed22244a618db4508675109441eb69959d41a191e689fcbdfd58",
85
+ "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1\\test.txt": "92e99a5062473a749254d90d9268ede03a804c9a8ac5806aa0a43e8b63222972",
86
+ "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1\\test.jsonl": "28b0a0493f5c77c5ecdb768d6e023400b191d6855f5542a68a0a87a766586ef3",
87
+ "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1\\manifest.json": "4a9d25d2a4abc6d9df8b008b49e34c060b917606c700dfa3b9f0dd4006030233",
88
+ "D:\\Sources\\VimeML\\outputs\\corpus-fast-v1\\stats.json": "fd7fc5077f0fdccd46591c1664735990a8f28fa8c93fa12a0e0030352ac7ec8b",
89
+ "D:\\Sources\\VimeML\\artifacts\\tokenizers\\ja-unigram-16k-v1\\tokenizer.model": "d9f1ba1e456ce72dd9c06a62b12e804cf14090c30179e6078cd51d03063b2982",
90
+ "D:\\Sources\\VimeML\\artifacts\\tokenizers\\ja-unigram-16k-v1\\manifest.json": "e88e66dee784ffa087a90e584793c2f41fb1f135499cf1bdd9eaa7cfb72ee921",
91
+ "D:\\Sources\\VimeML\\artifacts\\tokenizers\\ja-unigram-16k-v1\\stats.json": "9232bce676e86718940cdb11b5ad5d92b123816723ab68fa3d47212adca60ba8",
92
+ "D:\\Sources\\VimeML\\src\\vimeml\\tokenizer\\encode_parallel.py": "c194cb241d40b49669515f68f44278b55bcfbae210756f220b375cdee22ab009",
93
+ "D:\\Sources\\VimeML\\src\\vimeml\\tokenizer\\encode.py": "92bdbdda6fa80f4dbb975f8882c021d8c7c0f9017f669c89dfae31dc2eecaa69",
94
+ "D:\\Sources\\VimeML\\src\\vimeml\\tokenizer\\monitor.py": "c8cae279ea7faa0021b85bbc8dd2151510360a18db87982638920cc15a726b3a",
95
+ "D:\\Sources\\VimeML\\src\\vimeml\\tokenizer\\train.py": "3dc553d88962feb67fb919ee1f08879b2f9eac0d5b2783db7eed385505d3ee37"
96
+ },
97
+ "output_sha256": {
98
+ "train.tokens.bin": "5d8bafb458cdc510c0a9e9182d55d084c03cbc651eb348e387a965ac1b882834",
99
+ "train.offsets.bin": "8cee1724543d454e353fbd023cb40b0c0a9b2f9eb9b24b9e8ddae85afb46e762",
100
+ "train.rows.bin": "7cc9d8960e16d8df1c4ad1798255ade4def3bd066072439ebc3b7e8336674756",
101
+ "train.sources.bin": "e0e5184a6cc40fd6df0c4858cb56d79af838b07f07b86913b2a65395d325e616",
102
+ "validation.tokens.bin": "1bc211def7323bb620f164172a906e160c1860c1832c7d03c2de9699472f96cd",
103
+ "validation.offsets.bin": "e193c626736661525e2757186fb3588556d081a8019144fec0337de217dbfe1c",
104
+ "validation.rows.bin": "e63780d4e9b8378cee413683b6e861392dcb15a8af10308c63a8bcf68402bb79",
105
+ "validation.sources.bin": "dd57fbd72bf36133403751dc583943a5adf0409cf2a77bdb56ac6cc461944c34",
106
+ "test.tokens.bin": "ad5e5f6d5e5805bc7dbc883bbcd221df8d5699126c5a9210e86b86dad71a4e2c",
107
+ "test.offsets.bin": "89cdbcd19a39d0a2ff6a120aa1aeb526fc30aab03eec76b0fe50e51ca311dec3",
108
+ "test.rows.bin": "9653952ecabd27980f28ad7da21787b8e51046f55141a19cb21032e6de9b2faa",
109
+ "test.sources.bin": "473cc664829aa10e3c423389129427821b9985118febf6b276d01bd523056e37",
110
+ "stats.json": "563df68f55d54ed273681eb51145fd7b2cf4f2b4c1f86a053f07c666f9bc919b"
111
+ },
112
+ "corpus_quality_mode": "rules_with_approved_annotations",
113
+ "corpus_ready_for_lm_training": false,
114
+ "purpose": "Full token data for the accepted first corpus baseline; known extraction and near-duplicate limitations remain.",
115
+ "workers": 16,
116
+ "native_threads_per_worker": 1,
117
+ "signature": "53badf97e9290a595598f4c1cb270a118e5b92870dd406769752d119c31d963a",
118
+ "script_sha256": "c194cb241d40b49669515f68f44278b55bcfbae210756f220b375cdee22ab009"
119
+ }
inference/tokenizer.model ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d9f1ba1e456ce72dd9c06a62b12e804cf14090c30179e6078cd51d03063b2982
3
+ size 284118
inference/weights.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1cdac16913068bfdcfa94b139ecb34d945d0e9483a863373a6dca4397e3cd355
3
+ size 29562119
source/LICENSE ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ <signature of Ty Coon>, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
source/README.md ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ VimeML source/configuration from Git commit 9f0409ef7970c468efb622b207b1089de09651c1. GPL-2.0; see LICENSE.
2
+ The FP32 loader checks the original model and deployment code fingerprints.
3
+ Corpus texts, annotations, optimizer state and the separate Vime client are not included.
source/configs/corpus-parallel.toml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Local parallel runner: one command starts all workers and merges automatically.
2
+ [inputs]
3
+ fineweb_paths = [
4
+ "datasets/fineweb-2-edu-japanese/train-00010-of-00283.parquet",
5
+ "datasets/fineweb-2-edu-japanese/train-00030-of-00283.parquet",
6
+ "datasets/fineweb-2-edu-japanese/train-00050-of-00283.parquet",
7
+ "datasets/fineweb-2-edu-japanese/train-00070-of-00283.parquet",
8
+ "datasets/fineweb-2-edu-japanese/train-00090-of-00283.parquet",
9
+ "datasets/fineweb-2-edu-japanese/train-00110-of-00283.parquet",
10
+ "datasets/fineweb-2-edu-japanese/train-00130-of-00283.parquet",
11
+ "datasets/fineweb-2-edu-japanese/train-00150-of-00283.parquet",
12
+ "datasets/fineweb-2-edu-japanese/train-00170-of-00283.parquet",
13
+ ]
14
+ tatoeba_path = "datasets/Tatoeba/jpn_sentences.tsv"
15
+ calibration_ids = "annotations/calibration-documents-v1.json"
16
+
17
+ [build]
18
+ quality_mode = "rules_with_approved_annotations"
19
+ output_dir = "outputs/corpus-parallel-v1"
20
+ seed = 42
21
+ batch_size = 1024
22
+ max_join_chars = 2048
23
+ fineweb_documents_per_shard = 0
24
+ tatoeba_documents = 0
25
+ commit_every = 5000
26
+ progress_every = 5000
27
+
28
+ [review]
29
+ annotations = "annotations/approved-v1.jsonl"
30
+ require_all_annotations = true
31
+
32
+ [split]
33
+ train = 0.98
34
+ validation = 0.01
35
+ test = 0.01
source/configs/corpus-sharded.toml ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Same file on both machines; only --part and --output differ.
2
+ [inputs]
3
+ fineweb_paths = [
4
+ "datasets/fineweb-2-edu-japanese/train-00010-of-00283.parquet",
5
+ "datasets/fineweb-2-edu-japanese/train-00030-of-00283.parquet",
6
+ "datasets/fineweb-2-edu-japanese/train-00050-of-00283.parquet",
7
+ "datasets/fineweb-2-edu-japanese/train-00070-of-00283.parquet",
8
+ "datasets/fineweb-2-edu-japanese/train-00090-of-00283.parquet",
9
+ "datasets/fineweb-2-edu-japanese/train-00110-of-00283.parquet",
10
+ "datasets/fineweb-2-edu-japanese/train-00130-of-00283.parquet",
11
+ "datasets/fineweb-2-edu-japanese/train-00150-of-00283.parquet",
12
+ "datasets/fineweb-2-edu-japanese/train-00170-of-00283.parquet",
13
+ ]
14
+ tatoeba_path = "datasets/Tatoeba/jpn_sentences.tsv"
15
+ calibration_ids = "annotations/calibration-documents-v1.json"
16
+
17
+ [build]
18
+ quality_mode = "rules_with_approved_annotations"
19
+ output_dir = "outputs/corpus-parts"
20
+ seed = 42
21
+ batch_size = 1024
22
+ max_join_chars = 2048
23
+ fineweb_documents_per_shard = 0
24
+ tatoeba_documents = 0
25
+ commit_every = 5000
26
+ progress_every = 5000
27
+
28
+ [review]
29
+ annotations = "annotations/approved-v1.jsonl"
30
+ require_all_annotations = true
31
+
32
+ [split]
33
+ train = 0.98
34
+ validation = 0.01
35
+ test = 0.01
source/configs/ime-eval-v1.json ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "ime_diagnostic_v1",
3
+ "provenance": "Handwritten Japanese diagnostic cases, frozen before first evaluation. Synthetic homophone candidate lists, not AzooKey output. Training overlap unknown. References are non-exhaustive.",
4
+ "ranking": [
5
+ {"id":"hashi-food","group":"hashi","reading":"はし","context":"うどんを食べるために","candidates":["箸","橋","端"],"acceptable":["箸"]},
6
+ {"id":"hashi-river","group":"hashi","reading":"はし","context":"川を渡るための","candidates":["箸","橋","端"],"acceptable":["橋"]},
7
+ {"id":"hashi-edge","group":"hashi","reading":"はし","context":"机の中央ではなく","candidates":["箸","橋","端"],"acceptable":["端"]},
8
+ {"id":"au-friend","group":"au","reading":"あう","context":"駅で友人に","candidates":["会う","合う","遭う"],"acceptable":["会う"]},
9
+ {"id":"au-fit","group":"au","reading":"あう","context":"この服は体に","candidates":["会う","合う","遭う"],"acceptable":["合う"]},
10
+ {"id":"au-accident","group":"au","reading":"あう","context":"帰り道で事故に","candidates":["会う","合う","遭う"],"acceptable":["遭う"]},
11
+ {"id":"kikai-machine","group":"kikai","reading":"きかい","context":"工場で使う大きな","candidates":["機会","機械"],"acceptable":["機械"]},
12
+ {"id":"kikai-chance","group":"kikai","reading":"きかい","context":"もう一度挑戦する","candidates":["機会","機械"],"acceptable":["機会"]},
13
+ {"id":"kami-paper","group":"kami","reading":"かみ","context":"メモを書くための","candidates":["紙","髪","神"],"acceptable":["紙"]},
14
+ {"id":"kami-hair","group":"kami","reading":"かみ","context":"美容室で切ってもらった","candidates":["紙","髪","神"],"acceptable":["髪"]},
15
+ {"id":"kami-god","group":"kami","reading":"かみ","context":"神社に祀られている","candidates":["紙","髪","神"],"acceptable":["神"]},
16
+ {"id":"kouen-park","group":"kouen","reading":"こうえん","context":"子供たちが遊具で遊ぶ","candidates":["公園","講演","公演"],"acceptable":["公園"]},
17
+ {"id":"kouen-lecture","group":"kouen","reading":"こうえん","context":"大学の先生が研究成果について行う","candidates":["公園","講演","公演"],"acceptable":["講演"]},
18
+ {"id":"kouen-show","group":"kouen","reading":"こうえん","context":"劇場で行われるミュージカルの","candidates":["公園","講演","公演"],"acceptable":["公演"]},
19
+ {"id":"kanshou-art","group":"kanshou","reading":"かんしょう","context":"美術館で絵画を","candidates":["鑑賞","干渉"],"acceptable":["鑑賞"]},
20
+ {"id":"kanshou-interfere","group":"kanshou","reading":"かんしょう","context":"他人の私生活に過度に","candidates":["鑑賞","干渉"],"acceptable":["干渉"]},
21
+ {"id":"seikaku-personality","group":"seikaku","reading":"せいかく","context":"彼は明るく穏やかな","candidates":["性格","正確"],"acceptable":["性格"]},
22
+ {"id":"seikaku-accurate","group":"seikaku","reading":"せいかく","context":"この測定結果は非常に","candidates":["性格","正確"],"acceptable":["正確"]},
23
+ {"id":"hoshou-warranty","group":"hoshou","reading":"ほしょう","context":"製品が故障した場合の無料修理を","candidates":["保証","保障","補償"],"acceptable":["保証"]},
24
+ {"id":"hoshou-rights","group":"hoshou","reading":"ほしょう","context":"すべての国民の基本的人権を","candidates":["保証","保障","補償"],"acceptable":["保障"]},
25
+ {"id":"hoshou-damage","group":"hoshou","reading":"ほしょう","context":"事故による被害者の損害を","candidates":["保証","保障","補償"],"acceptable":["補償"]},
26
+ {"id":"ishi-stone","group":"ishi","reading":"いし","context":"川辺で拾った丸い","candidates":["石","意思","医師","意志"],"acceptable":["石"]},
27
+ {"id":"ishi-doctor","group":"ishi","reading":"いし","context":"病院で患者を診察する","candidates":["石","意思","医師","意志"],"acceptable":["医師"]},
28
+ {"id":"ishi-intent","group":"ishi","reading":"いし","context":"本人の同意や","candidates":["石","意思","医師","意志"],"acceptable":["意思","意志"],"note":"意思 is the preferred spelling here; 意志 is accepted conservatively as a meaningful variant."},
29
+ {"id":"atsui-weather","group":"atsui","reading":"あつい","context":"真夏の屋外はとても","candidates":["暑い","熱い","厚い"],"acceptable":["暑い"]},
30
+ {"id":"atsui-water","group":"atsui","reading":"あつい","context":"沸かしたばかりのお湯は","candidates":["暑い","熱い","厚い"],"acceptable":["熱い"]},
31
+ {"id":"atsui-book","group":"atsui","reading":"あつい","context":"この本はページが多くて","candidates":["暑い","熱い","厚い"],"acceptable":["厚い"]},
32
+ {"id":"naosu-repair","group":"naosu","reading":"なおす","context":"壊れたパソコンを","candidates":["直す","治す"],"acceptable":["直す"]},
33
+ {"id":"naosu-cure","group":"naosu","reading":"なおす","context":"薬を飲んで風邪を","candidates":["直す","治す"],"acceptable":["治す"]},
34
+ {"id":"kiku-medicine","group":"kiku","reading":"きく","context":"この薬は頭痛によく","candidates":["聞く","聴く","効く","利く"],"acceptable":["効く"]},
35
+ {"id":"kiku-listen","group":"kiku","reading":"きく","context":"会議で上司の説明を","candidates":["聞く","聴く","効く","利く"],"acceptable":["聞く","聴く"],"note":"Both listening spellings are accepted."},
36
+ {"id":"kiku-flexible","group":"kiku","reading":"きく","context":"小さな変更でも融通が","candidates":["聞く","聴く","効く","利く"],"acceptable":["利く"]}
37
+ ],
38
+ "suggestions": [
39
+ {"id":"weather","prompt":"今日はとても","references":["いい天気","良い天気","暑い","寒い","楽しい","忙しい","暖かい","寒かった","暑かった","よかった","良かった"]},
40
+ {"id":"formal-thanks","prompt":"本日はお忙しい中","references":["ありがとうございます","ありがとうございました","お越しいただき","ご参加いただき","お集まりいただき","お時間をいただき"]},
41
+ {"id":"busy-request","prompt":"お忙しいところ","references":["ありがとうございます","ありがとうございました","恐れ入ります","申し訳ありません","恐縮ですが","お手数ですが","すみません"]},
42
+ {"id":"confirmation","prompt":"ご確認のほど","references":["よろしくお願いいたします","よろしくお願いします","お願いいたします","お願いします","お願い申し上げます"]},
43
+ {"id":"walk","prompt":"駅まで歩いて","references":["行きます","行く","向かいます","向かう","移動","約","五分","5分","10分"]},
44
+ {"id":"password","prompt":"パスワードを入力して","references":["ログイン","ください","下さい","次へ","認証","送信"]},
45
+ {"id":"form","prompt":"お名前とメールアドレスを","references":["入力してください","ご入力ください","記入してください","お知らせください","登録してください","入力","記入"]},
46
+ {"id":"cold","prompt":"風邪をひいたので","references":["休み","休む","病院","薬","寝","早く寝","会社を休","学校を休","今日は休","自宅で","家で","休養"]},
47
+ {"id":"attachment","prompt":"添付した資料の内容を","references":["ご確認ください","確認してください","ご確認いただけますか","確認","ご確認"]},
48
+ {"id":"reply","prompt":"お返事を","references":["お待ち","いただけ","ください","お願い","頂け"]},
49
+ {"id":"application","prompt":"このアプリは","references":["無料","簡単","スマートフォン","使い","便利","利用","対応"]},
50
+ {"id":"study","prompt":"日本語を勉強する","references":["ため","上で","とき","時","方法","こと"]},
51
+ {"id":"morning","prompt":"明日の朝は","references":["早く","早起き","雨","晴れ","学校","仕事","出発","午前"]},
52
+ {"id":"dinner","prompt":"夕食のあとに","references":["散歩","お風呂","デザート","お茶","家族","テレビ","寝","勉強"]}
53
+ ]
54
+ }
source/configs/ime-homophones.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": "handchecked_homophones_v1",
3
+ "note": "Controlled same-reading candidate pools. Contexts and gold suitability still require blind verification. Alternative listening/intent spellings are excluded to reduce ambiguity.",
4
+ "groups": [
5
+ {"id":"hashi","reading":"はし","candidates":["箸","橋","端"]},
6
+ {"id":"au","reading":"あう","candidates":["会う","合う","遭う"]},
7
+ {"id":"kikai","reading":"きかい","candidates":["機会","機械"]},
8
+ {"id":"kami","reading":"かみ","candidates":["紙","髪","神"]},
9
+ {"id":"kouen","reading":"こうえん","candidates":["公園","講演","公演"]},
10
+ {"id":"kanshou","reading":"かんしょう","candidates":["鑑賞","干渉"]},
11
+ {"id":"seikaku","reading":"せいかく","candidates":["性格","正確"]},
12
+ {"id":"hoshou","reading":"ほしょう","candidates":["保証","保障","補償"]},
13
+ {"id":"ishi","reading":"いし","candidates":["石","医師"]},
14
+ {"id":"atsui","reading":"あつい","candidates":["暑い","熱い","厚い"]},
15
+ {"id":"naosu","reading":"なおす","candidates":["直す","治す"]},
16
+ {"id":"kiku","reading":"きく","candidates":["聞く","効く","利く"]}
17
+ ]
18
+ }
source/configs/inference-prompts.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {"kind": "daily", "prompt": "今日はとても"},
3
+ {"kind": "daily", "prompt": "明日の朝は"},
4
+ {"kind": "daily", "prompt": "駅まで歩いて"},
5
+ {"kind": "daily", "prompt": "この本を読んで"},
6
+ {"kind": "learning", "prompt": "日本語を勉強する"},
7
+ {"kind": "business", "prompt": "お忙しいところ"},
8
+ {"kind": "business", "prompt": "本日はお忙しい中"},
9
+ {"kind": "technology", "prompt": "このアプリは"},
10
+ {"kind": "daily", "prompt": "最近、仕事が忙しくて"},
11
+ {"kind": "travel", "prompt": "東京駅から電車で"},
12
+ {"kind": "digits", "prompt": "2026年10月"},
13
+ {"kind": "latin", "prompt": "iPhoneで日本語を"}
14
+ ]
source/configs/loader.toml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # Data validation only. No model is trained by this entry point.
2
+ token_dir = "artifacts/token-data/corpus-v1-16k"
3
+ index_dir = "artifacts/training-data/corpus-v1-c128"
4
+ context_length = 128
5
+ batch_size = 128
6
+ num_workers = 4
7
+ seed = 42
8
+ check_batches = 8
source/configs/phrase-demo-prompts.json ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {"id":"rain","scene":"天气","prompt":"今日は雨が降っているので、"},
3
+ {"id":"meeting","scene":"工作","prompt":"明日の会議までに、"},
4
+ {"id":"station","scene":"出行","prompt":"駅に着いたら、"},
5
+ {"id":"food","scene":"日常","prompt":"お腹が空いたので、"},
6
+ {"id":"thanks","scene":"商务","prompt":"本日はお忙しい中、"},
7
+ {"id":"confirmation","scene":"商务","prompt":"ご確認のほど、"},
8
+ {"id":"attachment","scene":"工作","prompt":"添付した資料を"},
9
+ {"id":"password","scene":"技术","prompt":"パスワードを入力して"},
10
+ {"id":"update","scene":"技术","prompt":"アプリを更新したら、"},
11
+ {"id":"travel","scene":"出行","prompt":"旅行に出かける前に、"},
12
+ {"id":"cold","scene":"日常","prompt":"風邪をひいたので、"},
13
+ {"id":"reading","scene":"日常","prompt":"寝る前に本を"},
14
+ {"id":"clothes","scene":"穿着","prompt":"外は寒いので、コートを"},
15
+ {"id":"movie","scene":"媒体","prompt":"昨日見た映画は"},
16
+ {"id":"practice","scene":"学习","prompt":"日本語が上手になるために、"},
17
+ {"id":"schedule","scene":"安排","prompt":"来週の月曜日は"},
18
+ {"id":"reply","scene":"工作","prompt":"お返事を"},
19
+ {"id":"dinner","scene":"日常","prompt":"夕食を食べた後、"},
20
+ {"id":"coffee","scene":"日常","prompt":"朝起きたら、コーヒーを"},
21
+ {"id":"friend","scene":"交流","prompt":"久しぶりに友達と"}
22
+ ]
source/configs/tokenizer.toml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Formal baseline: learn the vocabulary from a random sample of all train rows.
2
+ # LM training still uses the full corpus. Only a manual command starts training.
3
+ [paths]
4
+ corpus_dir = "outputs/corpus-fast-v1"
5
+ output_dir = "artifacts/tokenizers/ja-unigram-16k-v1"
6
+
7
+ [training]
8
+ model_type = "unigram"
9
+ vocab_size = 16384
10
+ character_coverage = 0.9995
11
+ byte_fallback = true
12
+ normalization_rule_name = "identity"
13
+ remove_extra_whitespaces = false
14
+ num_threads = 12
15
+ input_sentence_size = 2000000
16
+ seed = 42
17
+ max_sentence_length = 8192
18
+
19
+ [measurement]
20
+ # Independent processes; each native encoder uses one thread.
21
+ workers = 8
22
+ batch_size = 4096
23
+
24
+ [monitoring]
25
+ tensorboard = true
source/configs/train-smoke.toml ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # First manual run: pipeline correctness and measured throughput, not a final LM.
2
+ token_dir = "artifacts/token-data/corpus-v1-16k"
3
+ index_dir = "artifacts/training-data/corpus-v1-c128"
4
+ output_dir = "artifacts/models/tiny-ja-smoke-v1"
5
+ log_dir = "runs/training/tiny-ja-smoke-v1"
6
+
7
+ [model]
8
+ vocab_size = 16384
9
+ context_length = 128
10
+ d_model = 256
11
+ n_heads = 4
12
+ n_layers = 4
13
+ d_ff = 1024
14
+ dropout = 0.0
15
+
16
+ [training]
17
+ device = "cuda"
18
+ precision = "bf16"
19
+ seed = 42
20
+ cpu_threads = 4
21
+ batch_size = 128
22
+ num_workers = 4
23
+ bucket_multiplier = 64
24
+ gradient_accumulation = 1
25
+ max_steps = 300
26
+ learning_rate = 0.0003
27
+ min_learning_rate = 0.00003
28
+ warmup_steps = 30
29
+ weight_decay = 0.1
30
+ beta1 = 0.9
31
+ beta2 = 0.95
32
+ grad_clip = 1.0
33
+ log_every = 10
34
+ eval_every = 100
35
+ eval_batches = 16
36
+ checkpoint_every = 100
37
+ benchmark_warmup_steps = 20
source/configs/train-v1.toml ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # First formal baseline: exactly one full pass through the frozen corpus.
2
+ # 25,197,117 windows / batch128 = 196,853 updates, including the partial batch.
3
+ token_dir = "artifacts/token-data/corpus-v1-16k"
4
+ index_dir = "artifacts/training-data/corpus-v1-c128"
5
+ output_dir = "artifacts/models/tiny-ja-v1"
6
+ log_dir = "runs/training/tiny-ja-v1"
7
+
8
+ [model]
9
+ vocab_size = 16384
10
+ context_length = 128
11
+ d_model = 256
12
+ n_heads = 4
13
+ n_layers = 4
14
+ d_ff = 1024
15
+ dropout = 0.0
16
+
17
+ [training]
18
+ device = "cuda"
19
+ precision = "bf16"
20
+ seed = 42
21
+ cpu_threads = 4
22
+ batch_size = 128
23
+ num_workers = 4
24
+ bucket_multiplier = 64
25
+ gradient_accumulation = 1
26
+ epochs = 1
27
+ max_steps = 196853
28
+ learning_rate = 0.0003
29
+ min_learning_rate = 0.00003
30
+ warmup_steps = 2000
31
+ weight_decay = 0.1
32
+ beta1 = 0.9
33
+ beta2 = 0.95
34
+ grad_clip = 1.0
35
+ log_every = 100
36
+ eval_every = 5000
37
+ eval_batches = 128
38
+ checkpoint_every = 5000
39
+ benchmark_warmup_steps = 100
40
+ validate_full_at_end = true
source/requirements.txt ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python >=3.11. Install from PyPI after selecting the appropriate PyTorch build.
2
+ # Windows/Linux CUDA 12.8: install torch==2.10.0 from the cu128 index first;
3
+ # then install this file using the default PyPI index. See docs/training.md.
4
+ numpy>=2,<3; sys_platform != "darwin"
5
+ # Core ML Tools 9.0's Torch scalar casts are incompatible with NumPy 2.4+.
6
+ numpy==2.3.5; sys_platform == "darwin"
7
+ pyarrow==25.0.1
8
+ sentencepiece==0.2.1
9
+ # Keep Windows training frozen; Core ML 9 lists PyTorch 2.7 support.
10
+ torch==2.10.0; sys_platform != "darwin"
11
+ torch==2.7.1; sys_platform == "darwin"
12
+ coremltools==9.0; sys_platform == "darwin"
13
+ # K-means palettes also process FP32 QKV weights in the conservative route.
14
+ scikit-learn==1.5.1; sys_platform == "darwin"
15
+ tensorboard==2.20.0
16
+ # TensorBoard 2.20 imports pkg_resources, removed in newer setuptools.
17
+ setuptools==80.9.0
18
+ wandb==0.30.0
19
+ # Manual model release authentication/upload/download; never runs during training.
20
+ huggingface_hub==2.1.1
source/scripts/benchmarks/evaluate.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Score a frozen, verified synthetic benchmark with a local checkpoint."""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ ROOT = Path(__file__).resolve().parents[2]
6
+ sys.path.insert(0, str(ROOT / "src"))
7
+
8
+ from vimeml.benchmarks.evaluate import main
9
+
10
+ if __name__ == "__main__":
11
+ main()
source/scripts/benchmarks/evaluate_ajimee.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Evaluate a frozen AJIMEE candidate export using the local Tiny Japanese LM."""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ ROOT = Path(__file__).resolve().parents[2]
6
+ sys.path.insert(0, str(ROOT / "src"))
7
+
8
+ from vimeml.benchmarks.evaluate_ajimee import main
9
+
10
+ if __name__ == "__main__":
11
+ main()
source/scripts/benchmarks/export_cached_development.py ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Export successful generation caches for review, even if other batches failed."""
2
+ import argparse
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ ROOT = Path(__file__).resolve().parents[2]
8
+ sys.path.insert(0, str(ROOT / "src"))
9
+ from vimeml.benchmarks.development import export_cached_drafts
10
+ from vimeml.review.multi_key import output_lock
11
+
12
+
13
+ def main():
14
+ parser = argparse.ArgumentParser(description=__doc__)
15
+ parser.add_argument("--source", type=Path, required=True)
16
+ parser.add_argument("--output", type=Path, required=True)
17
+ parser.add_argument("--exclude", type=Path, default=ROOT / "artifacts/benchmarks/ajimee-jwtd-v2-v1/evaluation_items.json")
18
+ args = parser.parse_args()
19
+ try:
20
+ with output_lock(args.output):
21
+ stats = export_cached_drafts(args.source, args.output, args.exclude)
22
+ print(json.dumps(stats, ensure_ascii=False, indent=2))
23
+ except (OSError, ValueError, KeyError) as error:
24
+ parser.exit(2, f"Error: {error}\n")
25
+
26
+
27
+ if __name__ == "__main__":
28
+ main()
source/scripts/benchmarks/generate.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Generate and blind-check a synthetic benchmark; no training."""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ ROOT = Path(__file__).resolve().parents[2]
6
+ sys.path.insert(0, str(ROOT / "src"))
7
+
8
+ from vimeml.benchmarks.generate import main
9
+
10
+ if __name__ == "__main__":
11
+ main()
source/scripts/benchmarks/generate_development.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ """Generate and independently verify kana-conversion development drafts."""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ ROOT = Path(__file__).resolve().parents[2]
6
+ sys.path.insert(0,str(ROOT / 'src'))
7
+ from vimeml.benchmarks.development import main
8
+
9
+ if __name__ == '__main__':
10
+ main()
source/scripts/benchmarks/hybrid.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ """Prepare development inputs, cache model scores and evaluate hybrid ranking."""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ ROOT = Path(__file__).resolve().parents[2]
6
+ sys.path.insert(0, str(ROOT / "src"))
7
+ from vimeml.benchmarks.hybrid import main
8
+
9
+ if __name__ == "__main__":
10
+ main()
source/scripts/benchmarks/prepare_ajimee.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Prepare downloaded AJIMEE data for the AzooKey CLI (stdlib only)."""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ ROOT = Path(__file__).resolve().parents[2]
6
+ sys.path.insert(0, str(ROOT / "src"))
7
+
8
+ from vimeml.benchmarks.ajimee import main
9
+
10
+ if __name__ == "__main__":
11
+ main()
source/scripts/corpus/build.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Command entry point; implementation lives in src/vimeml."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ ROOT = Path(__file__).resolve().parents[2]
7
+ sys.path.insert(0, str(ROOT / "src"))
8
+
9
+ from vimeml.corpus.build import main
10
+
11
+ if __name__ == "__main__":
12
+ main()
source/scripts/corpus/merge.py ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Merge completed cleaning parts using the verified parallel exporter."""
2
+
3
+ import argparse
4
+ import os
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ ROOT = Path(__file__).resolve().parents[2]
9
+ sys.path.insert(0, str(ROOT / "src"))
10
+ from vimeml.data.merge_parts_fast import run
11
+
12
+
13
+ def main():
14
+ parser = argparse.ArgumentParser(description=__doc__)
15
+ parser.add_argument("--config", type=Path, default=ROOT / "configs/corpus-parallel.toml")
16
+ sources = parser.add_mutually_exclusive_group(required=True)
17
+ sources.add_argument("--parts", nargs="+", type=Path)
18
+ sources.add_argument("--parts-dir", type=Path)
19
+ parser.add_argument("--output", type=Path, required=True)
20
+ parser.add_argument("--work", type=Path)
21
+ parser.add_argument("--workers", type=int, default=min(8, os.cpu_count() or 1))
22
+ parser.add_argument("--buckets", type=int, default=64, choices=(1, 4, 16, 64, 256))
23
+ parser.add_argument("--cache-mb", type=int, default=128)
24
+ args = parser.parse_args()
25
+ parts = args.parts if args.parts is not None else sorted(p.parent for p in args.parts_dir.glob("*/manifest.json"))
26
+ if not parts:
27
+ parser.error("No completed parts found")
28
+ run(args.config, parts, args.output, workers=args.workers, buckets=args.buckets,
29
+ cache_mb=args.cache_mb, work=args.work)
30
+
31
+
32
+ if __name__ == "__main__":
33
+ main()
source/scripts/corpus/preprocess.py ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Command entry point; implementation lives in src/vimeml."""
2
+
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ ROOT = Path(__file__).resolve().parents[2]
7
+ sys.path.insert(0, str(ROOT / "src"))
8
+
9
+ from vimeml.data.preprocess_part import main
10
+
11
+ if __name__ == "__main__":
12
+ main()
source/scripts/deployment/ane_output_probe.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Manual reduced-output ANE experiment; not the deployment interface.
2
+
3
+ Usage: python scripts/deployment/ane_output_probe.py NEW_OUTPUT.mlpackage
4
+ Conversion, timing and accuracy checks start only when this command is run.
5
+ """
6
+ import argparse
7
+ import sys
8
+ import time
9
+ from pathlib import Path
10
+
11
+ ROOT = Path(__file__).resolve().parents[2]
12
+ sys.path.insert(0, str(ROOT / "src"))
13
+
14
+
15
+ def main():
16
+ parser = argparse.ArgumentParser(description=__doc__)
17
+ parser.add_argument("output", type=Path, help="New scratch .mlpackage path")
18
+ args = parser.parse_args()
19
+ out = args.output
20
+ if out.exists():
21
+ parser.error("Output exists; choose a new experiment path.")
22
+ if out.suffix != ".mlpackage":
23
+ parser.error("Output must end with .mlpackage.")
24
+ if sys.platform != "darwin":
25
+ parser.error("Run this experiment manually on Mac.")
26
+ import numpy as np, torch, coremltools as ct
27
+ from torch import nn
28
+ from vimeml.deployment.bundle import BundleLM
29
+ from vimeml.deployment.graph import ConversionGraph
30
+ lm = BundleLM(ROOT/"artifacts/deployment/tiny-ja-v1-inference-v1")
31
+ K = 16
32
+ class Head(nn.Module):
33
+ def __init__(s, model): super().__init__(); s.g = ConversionGraph(model)
34
+ def forward(s, input_ids, target_ids):
35
+ logp = torch.log_softmax(s.g(input_ids), dim=-1)
36
+ target = torch.gather(logp, 2, target_ids.long().unsqueeze(-1)).squeeze(-1)
37
+ top, idx = torch.topk(logp, K, dim=-1)
38
+ return target, top, idx.int()
39
+ h = Head(lm.model).eval()
40
+ ex = (torch.zeros(1,16,dtype=torch.int32), torch.zeros(1,16,dtype=torch.int32))
41
+ tr = torch.jit.trace(h, ex)
42
+ shapes = ct.EnumeratedShapes(shapes=[(1,L) for L in (16,32,64,128)], default=(1,16))
43
+ m = ct.convert(tr, convert_to="mlprogram", minimum_deployment_target=ct.target.iOS18, compute_precision=ct.precision.FLOAT16,
44
+ inputs=[ct.TensorType(name="input_ids", shape=shapes, dtype=np.int32), ct.TensorType(name="target_ids", shape=shapes, dtype=np.int32)],
45
+ outputs=[ct.TensorType(name="target_logprobs", dtype=np.float32), ct.TensorType(name="top_logprobs", dtype=np.float32), ct.TensorType(name="top_ids", dtype=np.int32)])
46
+ m.save(str(out))
47
+ for units in ("CPU_ONLY","CPU_AND_NE"):
48
+ mm = ct.models.MLModel(str(out), compute_units=getattr(ct.ComputeUnit, units)); row=[units]
49
+ for L in (16,64,128):
50
+ x={"input_ids":np.full((1,L),5,np.int32),"target_ids":np.full((1,L),6,np.int32)}; mm.predict(x); ts=[]
51
+ for _ in range(20): t=time.perf_counter(); mm.predict(x); ts.append((time.perf_counter()-t)*1000)
52
+ row.append(f"T{L}={np.median(ts):.2f}ms")
53
+ print(" ".join(row))
54
+ # accuracy vs FP32 on AJIMEE text
55
+ import json
56
+ items=json.load(open(ROOT/"artifacts/benchmarks/ajimee-jwtd-v2-v1/evaluation_items.json"))
57
+ mm = ct.models.MLModel(str(out), compute_units=ct.ComputeUnit.CPU_AND_NE)
58
+ diffs=[]; topagree=[]
59
+ for it in items[:100]:
60
+ ids=[2]+lm.processor.encode((it.get("context_text") or "")+it["expected_output"][0])[:127]
61
+ n=len(ids)-1; L=next(b for b in (16,32,64,128) if b>=n)
62
+ inp=np.zeros((1,L),np.int32); inp[0,:n]=ids[:-1]; tgt=np.zeros((1,L),np.int32); tgt[0,:n]=ids[1:]
63
+ o=mm.predict({"input_ids":inp,"target_ids":tgt})
64
+ with torch.no_grad(): ref=lm.model(torch.tensor([ids[:-1]])).float().log_softmax(-1)[0]
65
+ r=ref.gather(1,torch.tensor(ids[1:])[:,None]).squeeze(1).numpy()
66
+ diffs.append(abs(o["target_logprobs"][0,:n]-r).max())
67
+ topagree.append(np.mean(o["top_ids"][0,:n,0]==ref.argmax(-1).numpy()))
68
+ print(f"ANE target logprob max|diff| mean={np.mean(diffs):.4f} max={np.max(diffs):.4f}; top1 agree={np.mean(topagree):.4f}")
69
+
70
+
71
+ if __name__ == "__main__":
72
+ main()
source/scripts/deployment/compare_quantization.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Distribution-level check of a Core ML package against the FP32 bundle.
2
+
3
+ Distribution diagnostics supplement the strict logits, PAD and ranking checks.
4
+ They do not establish exact conversion equivalence. This reports KL(FP32 || model),
5
+ NLL on real text, top-1 agreement and, as a control, the same metrics for a
6
+ PyTorch simulation of symmetric INT8 block quantization. Read-only.
7
+ """
8
+ import argparse
9
+ import copy
10
+ import json
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ ROOT = Path(__file__).resolve().parents[2]
15
+ sys.path.insert(0, str(ROOT / "src"))
16
+
17
+ import numpy as np
18
+ import torch
19
+
20
+ from vimeml.deployment.bundle import BundleLM, environment
21
+ from vimeml.deployment.coreml import verify_package
22
+ from vimeml.training.data import file_sha
23
+
24
+
25
+ def simulate_int8(model, block):
26
+ """Mirror the conservative recipe: FP16 storage except QKV/positions, then INT8 per block."""
27
+ if block <= 0 or any(p.ndim == 2 and p.shape[1] % block for p in model.parameters()):
28
+ raise ValueError("Block size must be positive and divide every matrix input dimension.")
29
+ result = copy.deepcopy(model)
30
+ with torch.no_grad():
31
+ for name, parameter in result.named_parameters():
32
+ if parameter.ndim != 2:
33
+ continue
34
+ weight = parameter if ("qkv" in name or "position" in name) else parameter.half().float()
35
+ rows, columns = weight.shape
36
+ blocks = weight.reshape(rows, columns // block, block)
37
+ scale = blocks.abs().amax(-1, keepdim=True).clamp_min(1e-12) / 127
38
+ parameter.copy_((torch.round(blocks / scale).clamp(-127, 127) * scale).reshape(rows, columns))
39
+ return result
40
+
41
+
42
+ def texts(benchmark):
43
+ items = json.loads((benchmark / "evaluation_items.json").read_text(encoding="utf-8"))
44
+ return [(item.get("context_text") or "") + item["expected_output"][0] for item in items]
45
+
46
+
47
+ def compare(lm, model, mlmodel, sequences):
48
+ metrics = {key: [] for key in ("kl_coreml", "kl_simulated", "nll_fp32", "nll_coreml",
49
+ "top1_agreement", "max_logprob_diff")}
50
+ for ids in sequences:
51
+ tensor = torch.tensor([ids])
52
+ with torch.no_grad():
53
+ reference = lm.model(tensor)[0].float().log_softmax(-1)
54
+ simulated = model(tensor)[0].float().log_softmax(-1)
55
+ logits = mlmodel.predict({"input_ids": np.array([ids], dtype=np.int32)})["logits"][0]
56
+ coreml = torch.tensor(logits).log_softmax(-1)
57
+ probabilities = reference.exp()
58
+ metrics["kl_coreml"].append((probabilities * (reference - coreml)).sum(-1).mean().item())
59
+ metrics["kl_simulated"].append((probabilities * (reference - simulated)).sum(-1).mean().item())
60
+ targets = tensor[0, 1:, None]
61
+ metrics["nll_fp32"].append(-reference[:-1].gather(-1, targets).mean().item())
62
+ metrics["nll_coreml"].append(-coreml[:-1].gather(-1, targets).mean().item())
63
+ metrics["top1_agreement"].append((reference.argmax(-1) == coreml.argmax(-1)).float().mean().item())
64
+ metrics["max_logprob_diff"].append((reference - coreml).abs().max().item())
65
+ return {key: {"mean": float(np.mean(values)), "max": float(np.max(values))} for key, values in metrics.items()}
66
+
67
+
68
+ def main():
69
+ parser = argparse.ArgumentParser(description=__doc__)
70
+ parser.add_argument("--bundle", type=Path, default=ROOT / "artifacts/deployment/tiny-ja-v1-inference-v1")
71
+ parser.add_argument("--model", type=Path, required=True, help="Core ML experiment directory with model.mlpackage")
72
+ parser.add_argument("--benchmark", type=Path, default=ROOT / "artifacts/benchmarks/ajimee-jwtd-v2-v1")
73
+ parser.add_argument("--block-size", type=int, default=32)
74
+ parser.add_argument("--compute-units", choices=("CPU_ONLY", "CPU_AND_GPU", "CPU_AND_NE", "ALL"), default="CPU_ONLY")
75
+ parser.add_argument("--output", type=Path, help="Optional JSON report path; must not exist.")
76
+ args = parser.parse_args()
77
+ if args.output and args.output.exists():
78
+ parser.error("Output exists.")
79
+ lm = BundleLM(args.bundle)
80
+ manifest = verify_package(args.model)
81
+ if manifest["bundle"]["bundle_manifest_sha256"] != lm.metadata["bundle_manifest_sha256"]:
82
+ raise ValueError("Core ML model and FP32 bundle identities differ.")
83
+ if sys.platform != "darwin":
84
+ parser.error("Run Core ML prediction manually on Mac.")
85
+ import coremltools as ct
86
+ torch.set_num_threads(4)
87
+ mlmodel = ct.models.MLModel(str(args.model / "model.mlpackage"),
88
+ compute_units=getattr(ct.ComputeUnit, args.compute_units))
89
+ simulated = simulate_int8(lm.model, args.block_size).eval()
90
+ limit = lm.model.config.context_length - 1
91
+ encoded = [lm.processor.encode(text) for text in texts(args.benchmark)]
92
+ if not encoded or any(not ids for ids in encoded):
93
+ raise ValueError("Benchmark must have at least one nonempty target token per example.")
94
+ real = [[lm.special["bos"], *ids[:limit]] for ids in encoded]
95
+ rng = np.random.default_rng(0)
96
+ random = [[lm.special["bos"], *rng.integers(4, lm.model.config.vocab_size, limit).tolist()] for _ in range(5)]
97
+ report = {"format": "vimeml_distribution_diagnostic_v2", "model": str(args.model),
98
+ "compute_units": args.compute_units, "environment": environment(),
99
+ "bundle_manifest_sha256": lm.metadata["bundle_manifest_sha256"],
100
+ "coreml_manifest_sha256": file_sha(args.model / "manifest.json"),
101
+ "benchmark_items_sha256": file_sha(args.benchmark / "evaluation_items.json"),
102
+ "simulation": {"block_size": args.block_size,
103
+ "note": "FP32 scales and symmetric +/-127 rounding; not exact Core ML scale storage or kernels."},
104
+ "sampling": {"examples": len(encoded), "content_token_limit": limit,
105
+ "truncated_examples": sum(len(ids) > limit for ids in encoded),
106
+ "aggregation": "Mean of each example's position mean; not token-weighted corpus mean.",
107
+ "kl_top1_include_last_position": True, "nll_excludes_last_position": True,
108
+ "random_sequences": 5, "random_seed": 0},
109
+ "benchmark_text": compare(lm, simulated, mlmodel, real),
110
+ "random_tokens": compare(lm, simulated, mlmodel, random)}
111
+ text = json.dumps(report, ensure_ascii=False, indent=2)
112
+ print(text)
113
+ if args.output:
114
+ args.output.parent.mkdir(parents=True, exist_ok=True)
115
+ args.output.write_text(text + "\n", encoding="utf-8")
116
+
117
+
118
+ if __name__ == "__main__":
119
+ main()
source/scripts/deployment/compute_plan.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Report where Core ML would place each op of a package (CPU / GPU / Neural Engine).
2
+
3
+ Uses MLComputePlan on this Mac. Placement on iPhone can differ; confirm with a device run.
4
+ """
5
+ import argparse
6
+ import json
7
+ import shutil
8
+ import tempfile
9
+ from collections import Counter
10
+ from pathlib import Path
11
+
12
+
13
+ def plan(package, units):
14
+ import coremltools as ct
15
+ from coremltools.models.compute_plan import MLComputePlan
16
+ from coremltools.models.compute_device import MLCPUComputeDevice, MLGPUComputeDevice, MLNeuralEngineComputeDevice
17
+
18
+ names = {MLCPUComputeDevice: "cpu", MLGPUComputeDevice: "gpu", MLNeuralEngineComputeDevice: "ane"}
19
+ temporary = Path(tempfile.mkdtemp())
20
+ try:
21
+ compiled = ct.utils.compile_model(str(package), str(temporary / "model.mlmodelc"))
22
+ compute_plan = MLComputePlan.load_from_path(path=str(compiled), compute_units=getattr(ct.ComputeUnit, units))
23
+ program = compute_plan.model_structure.program
24
+ placement, cost, details = Counter(), Counter(), []
25
+ for function in program.functions.values():
26
+ for operation in function.block.operations:
27
+ usage = compute_plan.get_compute_device_usage_for_mlprogram_operation(operation)
28
+ if usage is None:
29
+ continue
30
+ device = names.get(type(usage.preferred_compute_device), "other")
31
+ estimate = compute_plan.get_estimated_cost_for_mlprogram_operation(operation)
32
+ weight = estimate.weight if estimate else 0.0
33
+ placement[device] += 1
34
+ cost[device] += weight
35
+ details.append({"op": operation.operator_name, "device": device, "cost": weight,
36
+ "supported": sorted({names.get(type(d), "other")
37
+ for d in usage.supported_compute_devices})})
38
+ return {"package": str(package), "compute_units": units, "ops_by_device": dict(placement),
39
+ "estimated_cost_by_device": {k: round(v, 4) for k, v in cost.items()}, "ops": details}
40
+ finally:
41
+ shutil.rmtree(temporary, ignore_errors=True)
42
+
43
+
44
+ def main():
45
+ parser = argparse.ArgumentParser(description=__doc__)
46
+ parser.add_argument("--model", type=Path, required=True, help="Experiment directory with model.mlpackage")
47
+ parser.add_argument("--compute-units", default="CPU_AND_NE", choices=("CPU_ONLY", "CPU_AND_GPU", "CPU_AND_NE", "ALL"))
48
+ parser.add_argument("--details", action="store_true", help="Print every op")
49
+ args = parser.parse_args()
50
+ report = plan(args.model / "model.mlpackage", args.compute_units)
51
+ if not args.details:
52
+ report.pop("ops")
53
+ print(json.dumps(report, indent=2))
54
+
55
+
56
+ if __name__ == "__main__":
57
+ main()
source/scripts/deployment/coreml.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Manual deployment CLI: no training, API calls or automatic stage chaining."""
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ ROOT = Path(__file__).resolve().parents[2]
6
+ sys.path.insert(0, str(ROOT / "src"))
7
+
8
+ from vimeml.deployment.cli import main
9
+
10
+ if __name__ == "__main__":
11
+ main()
source/scripts/deployment/coreml_ane.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Neural Engine variant: FP16 computation, enumerated input lengths, optional INT8 weights.
2
+
3
+ The conservative package computes in FP32, which the Neural Engine cannot execute, so every
4
+ op falls back to CPU. This entry converts the same traced graph with FP16 computation and a
5
+ small set of fixed lengths (callers right-pad to the next length; PAD never affects valid
6
+ positions under the causal mask), then optionally applies per-channel INT8 weight quantization.
7
+ """
8
+ import argparse
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ ROOT = Path(__file__).resolve().parents[2]
13
+ sys.path.insert(0, str(ROOT / "src"))
14
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
15
+
16
+ import numpy as np
17
+ import torch
18
+
19
+ from vimeml.deployment.bundle import BundleLM
20
+ from vimeml.deployment.coreml import coremltools, finish, inspect_spec, verify_package
21
+ from vimeml.deployment.graph import trace_graph
22
+ from vimeml.training.data import file_sha
23
+ from coreml_conservative import parameter_compression_config
24
+
25
+ LENGTHS = (16, 32, 64, 128)
26
+
27
+
28
+ def convert(bundle, output, lengths):
29
+ ct = coremltools()
30
+ lm = BundleLM(bundle)
31
+ shapes = ct.EnumeratedShapes(shapes=[(1, length) for length in lengths], default=(1, lengths[0]))
32
+ model = ct.convert(trace_graph(lm.model), source="pytorch", convert_to="mlprogram",
33
+ minimum_deployment_target=ct.target.iOS18, compute_precision=ct.precision.FLOAT16,
34
+ inputs=[ct.TensorType(name="input_ids", shape=shapes, dtype=np.int32)],
35
+ outputs=[ct.TensorType(name="logits", dtype=np.float32)], skip_model_load=True)
36
+ model.short_description = "Frozen TinyGPT; FP16 computation for the Neural Engine; enumerated lengths; no KV cache."
37
+ model.user_defined_metadata["bundle_manifest_sha256"] = lm.metadata["bundle_manifest_sha256"]
38
+ finish(output, model, {"kind": "fp16_ane", "minimum_ios": 18, "bundle": lm.metadata, "lengths": list(lengths),
39
+ "conversion_script_sha256": file_sha(Path(__file__)),
40
+ "interface": {"input_ids": f"int32 [1,T], T in {list(lengths)}; right-pad with PAD=0",
41
+ "logits": "float32 [1,T,16384]; FP16 computation; no softmax"}})
42
+
43
+
44
+ def compress(source, output):
45
+ ct = coremltools()
46
+ original = verify_package(source)
47
+ if original["kind"] != "fp16_ane":
48
+ raise ValueError("Compress an uncompressed fp16_ane conversion.")
49
+ from coremltools.optimize import coreml as opt
50
+ model = ct.models.MLModel(str(source / "model.mlpackage"), skip_model_load=True)
51
+ config = opt.OpLinearQuantizerConfig(mode="linear_symmetric", dtype="int8",
52
+ granularity="per_channel", weight_threshold=2048)
53
+ optimization_config, selection = parameter_compression_config(opt, model, config)
54
+ result = opt.linear_quantize_weights(model, config=optimization_config)
55
+ if not any(name.startswith("constexpr_") for name in inspect_spec(result)["operations"]):
56
+ raise ValueError("No compressed constexpr weights found.")
57
+ finish(output, result, {"kind": "linear8_fp16_ane", "minimum_ios": 18, "bundle": original["bundle"],
58
+ "lengths": original["lengths"], "interface": original["interface"],
59
+ "source_manifest_sha256": file_sha(source / "manifest.json"),
60
+ "conversion_script_sha256": file_sha(Path(__file__)),
61
+ "compression": {"method": "linear", "bits": 8, "granularity": "per_channel", "weight_threshold": 2048,
62
+ "selection": selection,
63
+ "scope": "Learned matrices only; masks and small constants unchanged; FP16 activations."}})
64
+
65
+
66
+ def main():
67
+ parser = argparse.ArgumentParser(description=__doc__)
68
+ sub = parser.add_subparsers(dest="command", required=True)
69
+ convert_parser = sub.add_parser("convert")
70
+ convert_parser.add_argument("--bundle", type=Path, required=True)
71
+ convert_parser.add_argument("--lengths", type=int, nargs="+", default=list(LENGTHS))
72
+ compress_parser = sub.add_parser("compress")
73
+ compress_parser.add_argument("--source", type=Path, required=True)
74
+ for command in (convert_parser, compress_parser):
75
+ command.add_argument("--output", type=Path, required=True)
76
+ args = parser.parse_args()
77
+ if args.output.exists():
78
+ parser.error("Output exists; use a new versioned directory.")
79
+ torch.set_num_threads(4)
80
+ if args.command == "convert":
81
+ lengths = sorted(set(args.lengths))
82
+ if lengths[0] < 1 or lengths[-1] != 128:
83
+ parser.error("Lengths must be positive and include 128.")
84
+ convert(args.bundle, args.output, lengths)
85
+ else:
86
+ compress(args.source, args.output)
87
+ print(f"Output: {args.output.resolve()}")
88
+
89
+
90
+ if __name__ == "__main__":
91
+ main()
source/scripts/deployment/coreml_conservative.py ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FP16 weight-storage baseline and weight-only quantization with FP32 computation.
2
+
3
+ Kept outside the frozen deployment sources so an existing inference bundle
4
+ remains verifiable. This converter's hash and exact precision policy are saved
5
+ in the package manifest. Stages preserve prior experiment directories.
6
+ """
7
+ import argparse
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ ROOT = Path(__file__).resolve().parents[2]
12
+ sys.path.insert(0, str(ROOT / "src"))
13
+
14
+ import numpy as np
15
+ import torch
16
+
17
+ from vimeml.deployment.bundle import BundleLM, read_json
18
+ from vimeml.deployment.coreml import coremltools, finish, inspect_spec, verify_package
19
+ from vimeml.deployment.graph import trace_graph
20
+ from vimeml.training.data import file_sha
21
+
22
+
23
+ def weight_storage_graph(ct, traced, context_length, target=18):
24
+ from coremltools.converters.mil import Builder as mb
25
+ from coremltools.converters.mil.mil.scope import ScopeInfo
26
+
27
+ program = ct.convert(traced, source="pytorch", convert_to="milinternal",
28
+ minimum_deployment_target=getattr(ct.target, f"iOS{target}"), compute_precision=ct.precision.FLOAT32,
29
+ inputs=[ct.TensorType(name="input_ids", shape=(1, ct.RangeDim(lower_bound=1,
30
+ upper_bound=context_length, default=min(16, context_length))), dtype=np.int32)],
31
+ outputs=[ct.TensorType(name="logits", dtype=np.float32)])
32
+ changed = {"fp16_linear_layers": 0, "fp16_token_embedding_tables": 0,
33
+ "fp32_qkv_layers": 0, "fp32_position_embedding_tables": 0}
34
+ for function in program.functions.values():
35
+ with function:
36
+ for op in list(function.operations):
37
+ if op.op_type == "linear":
38
+ if "attention.qkv" in op.weight.name:
39
+ changed["fp32_qkv_layers"] += 1
40
+ continue
41
+ with mb.scope(*[ScopeInfo(source=source, data=data)
42
+ for source, data in op.scopes.items()]), mb.set_before_op(op):
43
+ weight = mb.const(val=op.weight.val.astype(np.float16),
44
+ name=op.weight.name + "_storage_fp16")
45
+ arguments = {"weight": weight}
46
+ if op.bias is not None:
47
+ arguments["bias"] = mb.const(val=op.bias.val.astype(np.float16),
48
+ name=op.bias.name + "_storage_fp16")
49
+ result = mb.linear(x=op.x, name=op.name + "_compute_fp32", **arguments)
50
+ function.replace_uses_of_var_after_op(anchor_op=op,
51
+ old_var=op.outputs[0], new_var=result)
52
+ function.remove_ops([op])
53
+ changed["fp16_linear_layers"] += 1
54
+ elif (op.op_type == "gather" and op.x.val is not None and
55
+ op.x.val.dtype == np.float32):
56
+ if "position_embedding" in op.x.name:
57
+ changed["fp32_position_embedding_tables"] += 1
58
+ continue
59
+ if "token_embedding" not in op.x.name:
60
+ raise ValueError("Unexpected floating-point gather table; inspect the graph.")
61
+ with mb.scope(*[ScopeInfo(source=source, data=data)
62
+ for source, data in op.scopes.items()]), mb.set_before_op(op):
63
+ table = mb.const(val=op.x.val.astype(np.float16),
64
+ name=op.x.name + "_storage_fp16")
65
+ gathered = mb.gather(x=table, indices=op.indices, axis=op.axis,
66
+ name=op.name + "_storage_fp16")
67
+ result = mb.cast(x=gathered, dtype="fp32", name=op.name + "_compute_fp32")
68
+ function.replace_uses_of_var_after_op(anchor_op=op,
69
+ old_var=op.outputs[0], new_var=result)
70
+ function.remove_ops([op])
71
+ changed["fp16_token_embedding_tables"] += 1
72
+ if not all(changed.values()):
73
+ raise ValueError(f"Expected TinyGPT precision boundaries were not found: {changed}")
74
+ program.functions["main"].outputs[0].set_name("logits")
75
+ return program, changed
76
+
77
+
78
+ def convert(bundle, output, target=18):
79
+ ct = coremltools()
80
+ if output.exists():
81
+ raise ValueError("Output exists; choose a new conversion experiment.")
82
+ lm = BundleLM(bundle)
83
+ program, changed = weight_storage_graph(ct, trace_graph(lm.model), lm.model.config.context_length, target)
84
+ model = ct.convert(program, convert_to="mlprogram", minimum_deployment_target=getattr(ct.target, f"iOS{target}"),
85
+ compute_precision=ct.precision.FLOAT32, skip_model_load=True)
86
+ policy = {"recipe": "fp16_weights_fp32_compute_preserve_positions_qkv_v1",
87
+ "computation": "FP32; token embedding gather is FP16 then cast to FP32",
88
+ "fp16_parameters": "Token embedding; non-QKV linear weights and biases, including LM head",
89
+ "fp32_parameters": "Position embedding; attention QKV weights/biases; LayerNorm parameters",
90
+ "graph_changes": changed}
91
+ model.short_description = "Frozen TinyGPT; mixed FP16/FP32 weights, FP32 computation; no KV cache."
92
+ model.user_defined_metadata["bundle_manifest_sha256"] = lm.metadata["bundle_manifest_sha256"]
93
+ model.user_defined_metadata["precision_recipe"] = policy["recipe"]
94
+ finish(output, model, {"kind": "fp16_weights_fp32_compute", "minimum_ios": target, "bundle": lm.metadata,
95
+ "precision_policy": policy, "conversion_script_sha256": file_sha(Path(__file__)),
96
+ "interface": {"input_ids": "int32 [1,T], 1<=T<=128; right PAD only",
97
+ "logits": "float32 [1,T,16384]; FP32 computation; no softmax"}})
98
+
99
+
100
+ def parameter_compression_config(opt, model, config):
101
+ """Allow only parameter matrices; global defaults must leave other consts intact."""
102
+ metadata = opt.get_weights_metadata(model, weight_threshold=config.weight_threshold)
103
+ selected, excluded = [], []
104
+ configs = {}
105
+ for name, entry in metadata.items():
106
+ value = entry.val
107
+ consumers = [{"name": child.name, "type": child.op_type,
108
+ "inputs": dict(child.params_name_mapping)} for child in entry.child_ops]
109
+ record = {"name": name, "shape": [int(dim) for dim in value.shape],
110
+ "dtype": str(value.dtype), "elements": int(value.size), "consumers": consumers}
111
+ normalized_name = name.replace(".", "_")
112
+ embedding = normalized_name.startswith(("model_token_embedding_weight",
113
+ "model_position_embedding_weight"))
114
+ parameter_consumers = all(
115
+ (child.op_type == "linear" and child.params_name_mapping.get("weight") == name) or
116
+ (embedding and child.op_type == "gather" and child.params_name_mapping.get("x") == name)
117
+ for child in entry.child_ops)
118
+ if (not consumers or not parameter_consumers or value.ndim != 2 or
119
+ not np.issubdtype(value.dtype, np.floating)):
120
+ record.update(reason="Not a linear weight or named embedding parameter",
121
+ nonfinite_elements=int((~np.isfinite(value)).sum()))
122
+ excluded.append(record)
123
+ continue
124
+ if value.size <= config.weight_threshold:
125
+ record["reason"] = "Does not exceed weight threshold"
126
+ excluded.append(record)
127
+ continue
128
+ if not np.isfinite(value).all():
129
+ raise ValueError(f"Nonfinite learned parameter {name}; refusing compression.")
130
+ selected.append(record)
131
+ configs[name] = config
132
+ if not selected:
133
+ raise ValueError("No eligible learned parameter matrices found; inspect the model graph.")
134
+ selection = {"policy": "linear_weights_and_named_embedding_tables_v1",
135
+ "selected_constants": selected, "excluded_constants": excluded,
136
+ "selected_elements": sum(item["elements"] for item in selected)}
137
+ print(f"Compression selection: {len(selected)} parameter matrices; "
138
+ f"{len(excluded)} large constants excluded.", flush=True)
139
+ for item in excluded:
140
+ print(f" Excluded {item['name']}: {item['reason']}", flush=True)
141
+ return opt.OptimizationConfig(global_config=None, op_name_configs=configs), selection
142
+
143
+
144
+ def compress(source, output, validation, method, bits, group_size, block_size, granularity="per_block"):
145
+ """Same exact-package alignment gate, extended to this declared source kind."""
146
+ ct = coremltools()
147
+ if output.exists():
148
+ raise ValueError("Output exists; choose a new compression experiment.")
149
+ original = verify_package(source)
150
+ if original["kind"] != "fp16_weights_fp32_compute":
151
+ raise ValueError("Use this command only with an uncompressed conservative conversion.")
152
+ gate = read_json(validation)
153
+ if (gate.get("format") != "vimeml_alignment_v1" or not gate.get("passed") or
154
+ gate.get("coreml_manifest_sha256") != file_sha(source / "manifest.json")):
155
+ raise ValueError("First pass alignment for this exact source and supply its alignment.json.")
156
+ if method == "palette" and group_size and original["minimum_ios"] < 18:
157
+ raise ValueError("Grouped palette requires iOS18; use per-tensor palette or linear INT8 on iOS17.")
158
+ if method == "linear" and (granularity == "per_block" or bits == 4) and original["minimum_ios"] < 18:
159
+ raise ValueError("Blockwise/4bit linear quantization requires iOS18; use INT8 per-channel on iOS17.")
160
+ from coremltools.optimize import coreml as opt
161
+ if method == "palette":
162
+ try:
163
+ from sklearn.cluster import KMeans # noqa: F401: required by Core ML k-means fallback
164
+ except ImportError as error:
165
+ raise RuntimeError("Install the Mac requirements first; palette compression needs "
166
+ "scikit-learn==1.5.1 (uv pip install --python venv/coreml/bin/python "
167
+ "scikit-learn==1.5.1).") from error
168
+ model = ct.models.MLModel(str(source / "model.mlpackage"), skip_model_load=True)
169
+ if method == "palette":
170
+ config = opt.OpPalettizerConfig(mode="kmeans", nbits=bits, weight_threshold=2048,
171
+ granularity="per_grouped_channel" if group_size else "per_tensor", group_size=group_size or 32)
172
+ else:
173
+ config = opt.OpLinearQuantizerConfig(mode="linear_symmetric", dtype=f"int{bits}",
174
+ granularity=granularity, block_size=block_size if granularity == "per_block" else 0, weight_threshold=2048)
175
+ optimization_config, selection = parameter_compression_config(opt, model, config)
176
+ if method == "palette":
177
+ result = opt.palettize_weights(model, config=optimization_config)
178
+ else:
179
+ result = opt.linear_quantize_weights(model, config=optimization_config)
180
+ if not any(name.startswith("constexpr_") for name in inspect_spec(result)["operations"]):
181
+ raise ValueError("No compressed constexpr weights found.")
182
+ finish(output, result, {"kind": f"{method}{bits}_fp32_compute", "minimum_ios": original["minimum_ios"],
183
+ "bundle": original["bundle"], "interface": original["interface"],
184
+ "source_manifest_sha256": file_sha(source / "manifest.json"),
185
+ "fp16_alignment_sha256": file_sha(validation),
186
+ "conversion_script_sha256": file_sha(Path(__file__)),
187
+ "precision_policy": {"source_recipe": original["precision_policy"]["recipe"],
188
+ "computation": original["precision_policy"]["computation"]},
189
+ "compression": {"method": method, "bits": bits, "group_size": group_size,
190
+ "block_size": block_size if method == "linear" and granularity == "per_block" else None,
191
+ "granularity": granularity if method == "linear" else "per_grouped_channel" if group_size else "per_tensor",
192
+ "weight_threshold": 2048,
193
+ "selection": selection,
194
+ "scope": "Eligible learned matrices, including FP32 QKV; structural constants/masks unchanged; no activation quantization or retraining."}})
195
+
196
+
197
+ def main():
198
+ parser = argparse.ArgumentParser(description=__doc__)
199
+ parser.add_argument("--threads", type=int, default=4)
200
+ sub = parser.add_subparsers(dest="command", required=True)
201
+ convert_parser = sub.add_parser("convert", help="Mixed weight storage and FP32 computation, iOS18.")
202
+ convert_parser.add_argument("--bundle", type=Path, required=True)
203
+ convert_parser.add_argument("--target", type=int, choices=(17, 18), default=18)
204
+ compress_parser = sub.add_parser("compress", help="Compress this exact validated conservative baseline.")
205
+ compress_parser.add_argument("--source", type=Path, required=True)
206
+ compress_parser.add_argument("--fp16-alignment", type=Path, required=True)
207
+ compress_parser.add_argument("--method", choices=("palette", "linear"), default="palette")
208
+ compress_parser.add_argument("--bits", type=int, choices=(4, 8), default=4)
209
+ compress_parser.add_argument("--group-size", type=int, choices=(0, 8, 16, 32), default=16)
210
+ compress_parser.add_argument("--block-size", type=int, choices=(16, 32, 64, 128), default=32)
211
+ compress_parser.add_argument("--granularity", choices=("per_channel", "per_block"), default="per_block")
212
+ for command in (convert_parser, compress_parser):
213
+ command.add_argument("--output", type=Path, required=True)
214
+ args = parser.parse_args()
215
+ if args.threads < 1:
216
+ parser.error("threads must be positive.")
217
+ if args.output.exists():
218
+ parser.error("Output exists; use a new versioned directory.")
219
+ torch.set_num_threads(args.threads)
220
+ if args.command == "convert":
221
+ convert(args.bundle, args.output, args.target)
222
+ else:
223
+ compress(args.source, args.output, args.fp16_alignment, args.method, args.bits,
224
+ args.group_size, args.block_size, args.granularity)
225
+ print(f"Output: {args.output.resolve()}")
226
+
227
+
228
+ if __name__ == "__main__":
229
+ main()
source/scripts/deployment/package_handoff.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Archive Mac project snapshots, experiment evidence and Vime client for local handoff."""
2
+ import argparse
3
+ import hashlib
4
+ import json
5
+ import subprocess
6
+ import zipfile
7
+ from pathlib import Path
8
+ from datetime import date
9
+
10
+
11
+ SKIP_PARTS = {'.git', '__pycache__', '.pytest_cache', '.build', '.swiftpm', 'xcuserdata'}
12
+ SKIP_NAMES = {'.DS_Store'}
13
+
14
+
15
+ def digest(source):
16
+ if isinstance(source, bytes):
17
+ return hashlib.sha256(source).hexdigest()
18
+ with source.open('rb') as stream:
19
+ return hashlib.file_digest(stream, 'sha256').hexdigest()
20
+
21
+
22
+ def main():
23
+ parser = argparse.ArgumentParser(description=__doc__)
24
+ parser.add_argument('--project', type=Path, required=True)
25
+ parser.add_argument('--client', type=Path, required=True)
26
+ parser.add_argument('--output', type=Path, required=True)
27
+ args = parser.parse_args()
28
+ root, client, out = args.project.resolve(), args.client.resolve(), args.output.resolve()
29
+ if out.exists():
30
+ raise SystemExit('Output already exists; use a new handoff version.')
31
+ out.mkdir(parents=True)
32
+ manifest = {'format': 'vimeml_mac_handoff_v1', 'date': date.today().isoformat(),
33
+ 'vime_git_head': subprocess.check_output(['git', 'rev-parse', 'HEAD'], cwd=client, text=True).strip(),
34
+ 'vime_git_status': subprocess.check_output(['git', 'status', '--short'], cwd=client, text=True),
35
+ 'archives': {}, 'excluded': [],
36
+ 'policy': 'Local transfer only; snapshots are not automatic merge instructions or Git commits.'}
37
+
38
+ def entries(base, roots, prefix):
39
+ files = []
40
+ for name in roots:
41
+ source = base / name
42
+ if not source.exists():
43
+ continue
44
+ paths = [source] if source.is_file() else sorted(source.rglob('*'))
45
+ for path in paths:
46
+ relative = path.relative_to(base)
47
+ if not path.is_file():
48
+ continue
49
+ if (SKIP_PARTS.intersection(relative.parts) or path.name in SKIP_NAMES or path.suffix == '.pyc'
50
+ or path.name == '.env' or path.name.startswith('.env.')
51
+ or path.name in {'.pypirc', '.envrc'}):
52
+ manifest['excluded'].append(str(Path(prefix) / relative))
53
+ continue
54
+ if path.is_symlink():
55
+ raise SystemExit(f'Unexpected symlink requires review: {path}')
56
+ files.append(((Path(prefix) / relative).as_posix(), path))
57
+ return files
58
+
59
+ def archive(name, files):
60
+ indexed = []
61
+ with zipfile.ZipFile(out / name, 'w', compression=zipfile.ZIP_DEFLATED, compresslevel=3) as zip_file:
62
+ for relative, data in files:
63
+ value = digest(data)
64
+ if isinstance(data, bytes):
65
+ zip_file.writestr(relative, data)
66
+ size = len(data)
67
+ else:
68
+ zip_file.write(data, relative)
69
+ size = data.stat().st_size
70
+ indexed.append({'path': relative, 'bytes': size, 'sha256': value})
71
+ # Verify every entry against its source hash, including models and raw trace files.
72
+ with zipfile.ZipFile(out / name) as zip_file:
73
+ for entry in indexed:
74
+ with zip_file.open(entry['path']) as stream:
75
+ value = hashlib.file_digest(stream, 'sha256').hexdigest()
76
+ if value != entry['sha256']:
77
+ raise SystemExit(f'Archive verification failed: {entry["path"]}')
78
+ manifest['archives'][name] = {'file_count': len(indexed),
79
+ 'uncompressed_bytes': sum(e['bytes'] for e in indexed),
80
+ 'archive_bytes': (out / name).stat().st_size,
81
+ 'entries': indexed}
82
+ print(f'{name}: {len(indexed)} files, {(out / name).stat().st_size / 1048576:.1f} MiB; SHA256 verified', flush=True)
83
+
84
+ source_roots = ['annotations', 'configs', 'docs', 'examples', 'scripts', 'src', 'tests']
85
+ source_roots += [p.name for p in root.iterdir() if p.is_file() and p.name not in SKIP_NAMES]
86
+ archive('VimeML-source.zip', entries(root, source_roots, 'VimeML'))
87
+ results_roots = ['outputs', 'runs'] + [str(p.relative_to(root)) for p in (root / 'artifacts').iterdir()
88
+ if p.name not in {'models', 'token-data'}]
89
+ archive('VimeML-results.zip', entries(root, results_roots, 'VimeML'))
90
+ archive('VimeML-training-inputs.zip', entries(root, ['artifacts/models', 'artifacts/token-data'], 'VimeML'))
91
+
92
+ client_paths = subprocess.check_output(['git', 'ls-files', '-z', '--cached', '--others', '--exclude-standard'], cwd=client).decode().split('\0')
93
+ client_entries = entries(client, sorted(set(p for p in client_paths if p)), 'Vime')
94
+ patch = subprocess.check_output(['git', 'diff', '--binary', 'HEAD', '--', '.', ':(exclude)**/xcuserdata/**'], cwd=client)
95
+ client_entries += [('CLIENT_TRACKED_CHANGES.patch', patch),
96
+ ('CLIENT_GIT_STATUS.txt', manifest['vime_git_status'].encode()),
97
+ ('CLIENT_GIT_HEAD.txt', (manifest['vime_git_head'] + '\n').encode()),
98
+ ('Vime/Docs/MacStageHandoff.md', (root / 'docs/reports/mac-20261006/handoff.md').read_bytes()),
99
+ ('Vime/Docs/iPhoneKeyboardMemory.md', (root / 'docs/reports/mac-20261006/iphone-keyboard-memory.md').read_bytes())]
100
+ archive('Vime-client.zip', client_entries)
101
+ manifest['not_archived_roots'] = ['VimeML/venv (rebuild from requirements and environment snapshot)',
102
+ 'VimeML/handoff (this generated output)',
103
+ 'Vime/.git (base commit and patch preserved instead)']
104
+ (out / 'README.md').write_bytes((root / 'docs/reports/mac-20261006/handoff.md').read_bytes())
105
+ (out / 'MANIFEST.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + '\n')
106
+ sums = []
107
+ for path in sorted(out.iterdir()):
108
+ if path.is_file():
109
+ with path.open('rb') as stream:
110
+ value = hashlib.file_digest(stream, 'sha256').hexdigest()
111
+ sums.append(f'{value} {path.name}')
112
+ (out / 'SHA256SUMS.txt').write_text('\n'.join(sums) + '\n')
113
+ print(f'Handoff ready: {out}', flush=True)
114
+
115
+
116
+ if __name__ == '__main__':
117
+ main()
source/scripts/deployment/package_ios.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Package an evaluated INT8 candidate-reranking deployment without overwriting experiments."""
2
+ import argparse
3
+ import json
4
+ import shutil
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ ROOT = Path(__file__).resolve().parents[2]
9
+ sys.path.insert(0, str(ROOT / "src"))
10
+ from vimeml.deployment.coreml import verify_package
11
+ from vimeml.training.data import file_sha
12
+
13
+
14
+ def package(source, compiled, tokenizer, review, output):
15
+ if output.exists():
16
+ raise ValueError("Output exists; choose a new deployment version.")
17
+ model = verify_package(source)
18
+ decision = json.loads(review.read_text())
19
+ if (model["kind"] != "linear8_fp32_compute" or model["minimum_ios"] != 18 or
20
+ model["compression"]["bits"] != 8 or
21
+ decision["source_manifest_sha256"] != file_sha(source / "manifest.json") or
22
+ decision["decision"] != "integrate_cpu_candidate_reranking_for_simulator_validation"):
23
+ raise ValueError("Review/source identity or reviewed INT8 deployment policy mismatch.")
24
+ if file_sha(tokenizer) != model["bundle"]["tokenizer_sha256"]:
25
+ raise ValueError("Wrong tokenizer for this frozen model.")
26
+ if not (compiled / "coremldata.bin").is_file():
27
+ raise ValueError("Supply an iOS18 coremlcompiler output directory.")
28
+ inventory = {p.relative_to(compiled).as_posix(): file_sha(p)
29
+ for p in sorted(compiled.rglob("*")) if p.is_file()}
30
+ output.mkdir(parents=True)
31
+ resources = output / "Resources"
32
+ resources.mkdir()
33
+ shutil.copytree(compiled, resources / "TinyJapaneseINT8.mlmodelc")
34
+ shutil.copyfile(tokenizer, resources / "VimeJapaneseTokenizer.model")
35
+ manifest = {"format": "vime_ios_lm_v1", "minimum_ios": 18, "vocab_size": 16384,
36
+ "context_length": 128, "compute_units": "CPU_ONLY", "tokenizer_sha256": file_sha(tokenizer),
37
+ "compiled_files_sha256": inventory, "source_manifest_sha256": file_sha(source / "manifest.json"),
38
+ "bundle_manifest_sha256": model["bundle"]["bundle_manifest_sha256"], "kind": model["kind"],
39
+ "quantization": decision["quantization"],
40
+ "strict_fp32_logits_alignment_passed": decision["strict_fp32_alignment_passed"],
41
+ "quality_review": "See the dated review.json; this packaging action does not validate the current client or installed device build.",
42
+ "integration_snapshot": "examples/ios/integration: first candidate-reranking integration, not the latest Vime client"}
43
+ (resources / "VimeLMManifest.json").write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n")
44
+ shutil.copyfile(review, output / "review.json")
45
+ shutil.copyfile(source / "manifest.json", output / "coreml-source-manifest.json")
46
+ shutil.copytree(ROOT / "examples/ios/VimeSentencePiece", output / "Dependencies/VimeSentencePiece")
47
+ shutil.copytree(ROOT / "examples/ios/integration", output / "Integration")
48
+ (output / "README.md").write_text("""# Vime iOS18 INT8 resource/reference package
49
+
50
+ Weight-only symmetric INT8 block32, FP32 computation, CPU_ONLY, iOS18.
51
+ Resources and tokenizer hashes are in Resources/VimeLMManifest.json.
52
+ Official SentencePiece0.2.1 and third-party license notices are preserved.
53
+
54
+ review.json is the dated first-integration review, not a fresh device acceptance.
55
+ Strict FP32 logits alignment failed; fixture/PAD/causal and dev/AJIMEE evidence
56
+ supported a limited candidate-reranking integration. Packaging does not run tests.
57
+
58
+ Integration/ is the first examples/ios/integration snapshot. It predates the latest
59
+ Vime client's sentence context, next-word suggestions, preferences and cancellation.
60
+ Use the latest Vime repository (or the archived Vime-client.zip) for those behaviors.
61
+ This package does not attest installed build identity, enabled settings or device stability.
62
+ See docs/coreml.md and docs/reports/mac-20261006/ for the subsequent device evidence.
63
+ """, encoding="utf-8")
64
+ print(output.resolve())
65
+
66
+
67
+ def main():
68
+ parser = argparse.ArgumentParser(description=__doc__)
69
+ for name in ("source", "compiled", "tokenizer", "review", "output"):
70
+ parser.add_argument("--" + name, type=Path, required=True)
71
+ args = parser.parse_args()
72
+ package(args.source, args.compiled, args.tokenizer, args.review, args.output)
73
+
74
+
75
+ if __name__ == "__main__":
76
+ main()
source/scripts/deployment/summarize_keyboard_trace.py ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Summarize exported Instruments sysmon-process XML for Vime processes."""
2
+ import argparse
3
+ import json
4
+ import statistics
5
+ import xml.etree.ElementTree as ET
6
+ from pathlib import Path
7
+
8
+
9
+ def summarize(source):
10
+ root = ET.parse(source).getroot()
11
+ columns = [c.findtext('mnemonic') for c in root.find('.//schema').findall('col')]
12
+ references = {e.attrib['id']: e for e in root.iter() if 'id' in e.attrib}
13
+
14
+ def resolve(element):
15
+ while 'ref' in element.attrib:
16
+ element = references[element.attrib['ref']]
17
+ return element
18
+
19
+ def number(element):
20
+ element = resolve(element)
21
+ return None if element.tag == 'sentinel' or element.text is None else float(element.text)
22
+
23
+ processes = {}
24
+ for row in root.findall('.//row'):
25
+ values = dict(zip(columns, row))
26
+ label = resolve(values['process']).get('fmt', '')
27
+ if not (label.startswith('VimeKeyboard (') or label.startswith('Vime (')):
28
+ continue
29
+ footprint = number(values['memory-physical-footprint'])
30
+ if footprint is None:
31
+ continue
32
+ item = {'seconds': number(values['time']) / 1e9,
33
+ 'physical_mib': footprint / 1048576,
34
+ 'resident_mib': number(values['memory-resident-size']) / 1048576,
35
+ 'private_resident_mib': number(values['memory-real-private']) / 1048576,
36
+ 'shared_resident_mib': number(values['memory-real-shared']) / 1048576,
37
+ 'threads': number(values['thread-count']),
38
+ 'recently_died': bool(number(values['recently-died']))}
39
+ processes.setdefault(label, []).append(item)
40
+ summary = {}
41
+ for label, samples in processes.items():
42
+ samples.sort(key=lambda s: s['seconds'])
43
+ peak = max(samples, key=lambda s: s['physical_mib'])
44
+ windows = []
45
+ for start in range(0, int(samples[-1]['seconds']) + 1, 30):
46
+ selected = [s['physical_mib'] for s in samples if start <= s['seconds'] < start + 30]
47
+ if selected:
48
+ windows.append({'start_seconds': start, 'end_seconds': start + 30,
49
+ 'samples': len(selected), 'min_mib': min(selected),
50
+ 'median_mib': statistics.median(selected), 'max_mib': max(selected)})
51
+ intervals = [b['seconds'] - a['seconds'] for a, b in zip(samples, samples[1:])]
52
+ summary[label] = {'sample_count': len(samples), 'first': samples[0], 'last': samples[-1],
53
+ 'sampled_peak': peak, 'sampled_min_mib': min(s['physical_mib'] for s in samples),
54
+ 'resident_metrics': {key: {'first_mib': samples[0][key],
55
+ 'sampled_peak_mib': max(s[key] for s in samples),
56
+ 'last_mib': samples[-1][key]}
57
+ for key in ('private_resident_mib', 'shared_resident_mib', 'resident_mib')},
58
+ 'median_interval_seconds': statistics.median(intervals) if intervals else None,
59
+ 'recently_died_samples': sum(s['recently_died'] for s in samples),
60
+ 'windows': windows, 'samples': samples}
61
+ return {'source': str(source), 'metric': 'sysmon physical footprint; sampled peak, not lifetime high-water mark',
62
+ 'processes': summary}
63
+
64
+
65
+ if __name__ == '__main__':
66
+ parser = argparse.ArgumentParser(description=__doc__)
67
+ parser.add_argument('--input', type=Path, required=True)
68
+ parser.add_argument('--output', type=Path, required=True)
69
+ args = parser.parse_args()
70
+ if args.output.exists():
71
+ parser.error("Output exists; choose a new report path.")
72
+ report = summarize(args.input)
73
+ args.output.parent.mkdir(parents=True, exist_ok=True)
74
+ with args.output.open('x', encoding='utf-8') as stream:
75
+ stream.write(json.dumps(report, indent=2) + '\n')
76
+ print(json.dumps({k: {key: value for key, value in v.items() if key != 'samples'}
77
+ for k, v in report['processes'].items()}, indent=2))
source/scripts/deployment/summarize_memory.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Summarize preserved fresh-process simulator memory audits (MiB, not iPhone estimates)."""
2
+ import argparse
3
+ import json
4
+ from pathlib import Path
5
+
6
+
7
+ def summarize(directory):
8
+ reports = {name: json.loads((directory / (name + '.json')).read_text())
9
+ for name in ('direct', 'reload', 'engine', 'lm')}
10
+ repeat = directory / 'lm_words-repeat.json'
11
+ if repeat.exists():
12
+ reports['lm_words'] = json.loads(repeat.read_text())
13
+ rows = []
14
+ for name in ('engine', 'lm', 'lm_words'):
15
+ if name not in reports:
16
+ continue
17
+ report = reports[name]
18
+ steady = next(s for s in report['stages'] if s['stage'] == 'worker_cycles_61_80')['after']['physical_mib']
19
+ values = [x['memory']['physical_mib'] for x in report['checkpoints']]
20
+ rows.append({'scenario': name, 'baseline_mib': report['baseline']['physical_mib'],
21
+ 'warm_after_80_cycles_mib': steady, 'process_peak_mib': report['final']['process_lifetime_peak_mib'],
22
+ 'warm_checkpoints_range_mib': max(values) - min(values),
23
+ 'after_worker_release_mib': report['final']['physical_mib'],
24
+ 'cycles_with_suggestions': report.get('cycles_with_published_suggestions')})
25
+ direct = reports['direct']
26
+ deltas = {}
27
+ for phase in ('scores', 'next_words'):
28
+ values = [x['memory']['physical_mib'] for x in direct['checkpoints'] if x.get('stage') == phase]
29
+ deltas[phase + '_20_to_100_growth_mib'] = values[-1] - values[0]
30
+ reload_values = [x['memory']['physical_mib'] for x in reports['reload']['checkpoints']]
31
+ prior = json.loads((directory / 'prior-keyboard-trace.json').read_text())
32
+ failed = directory / 'lm_words-failed.json'
33
+ summary = {'scope': 'optimized iOS27.0 arm64 Simulator test host, CPU_ONLY; not actual keyboard extension',
34
+ 'rows': rows, 'bounded_growth': deltas,
35
+ 'reload_1_to_20_growth_mib': reload_values[-1] - reload_values[0],
36
+ 'reload_10_to_20_growth_mib': reload_values[-1] - reload_values[9],
37
+ 'first_lm_words_failed': failed.exists(),
38
+ 'prior_extension_sampled_peak_mib': prior['peak_physical_mib'],
39
+ 'prior_extension_lm_loaded_proven': False,
40
+ 'limitations': ['Single runs with varying baselines; no fixed keyboard memory limit inferred.',
41
+ 'No new device connection, installation or recording.',
42
+ 'A bounded plateau does not exclude all leaks.',
43
+ 'Prior extension trace settings/source/workload do not prove LM-active measurement.']}
44
+ return summary
45
+
46
+
47
+ if __name__ == '__main__':
48
+ parser = argparse.ArgumentParser(description=__doc__)
49
+ parser.add_argument('--directory', type=Path, required=True)
50
+ parser.add_argument('--output', type=Path, help='New report path; default DIRECTORY/summary.json')
51
+ args = parser.parse_args()
52
+ output = args.output or args.directory / 'summary.json'
53
+ if output.exists():
54
+ parser.error('Output exists; use --output with a new report path.')
55
+ report = summarize(args.directory)
56
+ output.parent.mkdir(parents=True, exist_ok=True)
57
+ with output.open('x', encoding='utf-8') as stream:
58
+ stream.write(json.dumps(report, indent=2) + '\n')
59
+ print(json.dumps(report, indent=2))
source/scripts/publishing/hub.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Manual Hub upload/download. Network work starts only with an explicit subcommand."""
2
+ import argparse
3
+ import json
4
+ import sys
5
+ from pathlib import Path
6
+
7
+ sys.path.insert(0, str(Path(__file__).resolve().parent))
8
+ from prepare_hf import verifier
9
+
10
+
11
+ def main():
12
+ parser = argparse.ArgumentParser(description=__doc__)
13
+ sub = parser.add_subparsers(dest='command', required=True)
14
+ upload = sub.add_parser('upload', help='Manually upload only the verified inventory into the existing model repo main branch.')
15
+ upload.add_argument('--release', type=Path, required=True)
16
+ download = sub.add_parser('download', help='Download a fixed Hub commit into a fresh directory, then checksum-verify.')
17
+ download.add_argument('--revision', required=True, help='Full 40-character Hub commit from upload output')
18
+ download.add_argument('--output', type=Path, required=True)
19
+ for command in (upload, download):
20
+ command.add_argument('--repo-id', default='Voltline/vimeml-tiny-ja-v1')
21
+ args = parser.parse_args()
22
+ check = verifier()
23
+ if args.command == 'upload':
24
+ manifest, files = check.verify(args.release)
25
+ if manifest['repo_id'] != args.repo_id:
26
+ parser.error('Release and target repository identities differ.')
27
+ from huggingface_hub import CommitOperationAdd, HfApi
28
+ api = HfApi()
29
+ parent = api.repo_info(args.repo_id, repo_type='model', revision='main').sha
30
+ result = api.create_commit(repo_id=args.repo_id, repo_type='model', revision='main', parent_commit=parent,
31
+ operations=[CommitOperationAdd(path_in_repo=name, path_or_fileobj=str(args.release / name)) for name in files],
32
+ commit_message='Release frozen VimeML tiny-ja-v1 FP32 and Core ML INT8 (GPL-2.0)')
33
+ print(json.dumps({'repo_id': args.repo_id, 'hub_commit': result.oid,
34
+ 'commit_url': result.commit_url, 'uploaded_files': len(files)}, indent=2))
35
+ else:
36
+ if len(args.revision) != 40 or any(c not in '0123456789abcdef' for c in args.revision):
37
+ parser.error('Use a full immutable 40-character Hub commit, not main.')
38
+ if args.output.exists():
39
+ parser.error('Output exists; use a fresh download directory.')
40
+ from huggingface_hub import snapshot_download
41
+ snapshot_download(repo_id=args.repo_id, repo_type='model', revision=args.revision, local_dir=str(args.output))
42
+ manifest, files = check.verify(args.output)
43
+ if manifest['repo_id'] != args.repo_id:
44
+ raise ValueError('Downloaded release belongs to a different repository.')
45
+ print(json.dumps({'repo_id': args.repo_id, 'hub_commit': args.revision,
46
+ 'verified_files': len(files), 'source_commit': manifest['source_commit']}, indent=2))
47
+
48
+
49
+ if __name__ == '__main__':
50
+ main()
source/scripts/publishing/prepare_hf.py ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Prepare or verify a local GPL-2.0 v1 release. Never trains, converts or uploads."""
2
+ import argparse
3
+ import importlib.util
4
+ import json
5
+ import re
6
+ import shutil
7
+ import subprocess
8
+ import sys
9
+ from datetime import datetime, timezone
10
+ from pathlib import Path
11
+
12
+ ROOT = Path(__file__).resolve().parents[2]
13
+ sys.path.insert(0, str(ROOT / 'src'))
14
+
15
+
16
+ def verifier():
17
+ spec = importlib.util.spec_from_file_location('vimeml_release_verifier', ROOT / 'templates/huggingface/verify_release.py')
18
+ module = importlib.util.module_from_spec(spec)
19
+ spec.loader.exec_module(module)
20
+ return module
21
+
22
+
23
+ def write_json(path, value):
24
+ path.parent.mkdir(parents=True, exist_ok=True)
25
+ path.write_text(json.dumps(value, ensure_ascii=False, indent=2, allow_nan=False) + '\n', encoding='utf-8', newline='\n')
26
+
27
+
28
+ def git(*args):
29
+ return subprocess.check_output(['git', *args], cwd=ROOT).decode('utf-8').strip()
30
+
31
+
32
+ def copy_checked(source, target, digest=None):
33
+ check = verifier()
34
+ source = Path(source)
35
+ if source.is_symlink() or not source.is_file():
36
+ raise ValueError(f'Expected a regular source file: {source}')
37
+ expected = digest or check.sha(source)
38
+ target.parent.mkdir(parents=True, exist_ok=True)
39
+ if target.exists():
40
+ raise ValueError(f'Refusing to overwrite: {target}')
41
+ shutil.copyfile(source, target)
42
+ if check.sha(target) != expected:
43
+ raise ValueError(f'Copy checksum mismatch: {target}')
44
+
45
+
46
+ def evaluation_summary(bundle_manifest_sha, coreml_manifest_sha):
47
+ from vimeml.benchmarks.evaluate_ajimee import load_export, summarize
48
+ from vimeml.deployment.bundle import read_json
49
+ from vimeml.deployment.validation import compare_rows
50
+ sha = verifier().sha
51
+ hashes, results = {}, {}
52
+
53
+ def read(path):
54
+ hashes[path.relative_to(ROOT).as_posix()] = sha(path)
55
+ return read_json(path)
56
+
57
+ def rows(path, metadata):
58
+ expected = metadata['files_sha256']['scores.jsonl']
59
+ if sha(path) != expected:
60
+ raise ValueError(f'Score file identity mismatch: {path}')
61
+ hashes[path.relative_to(ROOT).as_posix()] = expected
62
+ return [json.loads(line) for line in path.read_text(encoding='utf-8').splitlines()]
63
+
64
+ for role, benchmark, baseline in [('dev', 'ime-dev-v2', 'tiny-ja-v1-dev-v2-scores'),
65
+ ('ajimee', 'ajimee-jwtd-v2-v1', 'tiny-ja-v1-ajimee')]:
66
+ directory = ROOT / f'outputs/deployment/conservative-int8-b32-{role}-v1'
67
+ report = read(directory / 'metrics.json')
68
+ if (report['model']['coreml_manifest_sha256'] != coreml_manifest_sha or
69
+ report['model']['bundle_manifest_sha256'] != bundle_manifest_sha):
70
+ raise ValueError('Saved evaluation uses a different model.')
71
+ current = rows(directory / 'scores.jsonl', report)
72
+ metrics = summarize(current)
73
+ if metrics != report['metrics']:
74
+ raise ValueError('Saved metrics do not match their score rows.')
75
+ frozen, provenance, inputs = load_export(ROOT / 'artifacts/benchmarks' / benchmark)
76
+ if frozen != report['benchmark_manifest'] or provenance != report['export_provenance'] or len(inputs) != len(current):
77
+ raise ValueError('Frozen candidate export identity changed.')
78
+ for left, right in zip(inputs, current):
79
+ if any(left[key] != right[key] for key in ('id', 'query', 'left_context', 'answers', 'candidates')):
80
+ raise ValueError('Saved candidate/input mismatch.')
81
+ prior_directory = ROOT / 'outputs/ime-eval' / baseline
82
+ prior_path = prior_directory / ('manifest.json' if (prior_directory / 'manifest.json').exists() else 'metrics.json')
83
+ prior_report = read(prior_path)
84
+ if any(prior_report['model'][key] != report['model'][key]
85
+ for key in ('checkpoint_sha256', 'tokenizer_sha256')):
86
+ raise ValueError('FP32 baseline belongs to different frozen weights or tokenizer.')
87
+ prior = rows(prior_directory / 'scores.jsonl', prior_report)
88
+ fp32_metrics = summarize(prior)
89
+ comparison = compare_rows(prior, current)
90
+ reported_before = prior
91
+ if role == 'dev':
92
+ source = ROOT / 'outputs/deployment/conservative-dev-v3'
93
+ source_metadata = read(source / 'metrics.json')
94
+ reported_before = rows(source / 'scores.jsonl', source_metadata)
95
+ if compare_rows(reported_before, current) != report['comparison']:
96
+ raise ValueError('Saved comparison cannot be reproduced from its actual baseline.')
97
+ results[role] = {'original_order': metrics['all']['azookey'],
98
+ 'fp32_lm': fp32_metrics['all']['lm_context_sum'], 'int8_lm': metrics['all']['lm_context_sum'],
99
+ 'windows_fp32_comparison': {k: v for k, v in comparison.items() if k != 'changes'},
100
+ 'original_report_comparison_baseline': 'conservative Core ML' if role == 'dev' else 'Windows FP32',
101
+ 'original_report_comparison': {k: v for k, v in report['comparison'].items() if k != 'changes'},
102
+ 'benchmark_source_sha256': frozen['source_sha256'], 'candidate_export_sha256': provenance['files_sha256']['azookey-candidates.json']}
103
+ alignment = read(ROOT / 'outputs/deployment/conservative-int8-b32-alignment-v1/alignment.json')
104
+ if alignment['coreml_manifest_sha256'] != coreml_manifest_sha:
105
+ raise ValueError('Alignment belongs to a different model.')
106
+ distribution = read(ROOT / 'outputs/deployment/conservative-int8-b32-distribution-v1/report.json')
107
+ training = read(ROOT / 'artifacts/models/tiny-ja-v1/summary.json')
108
+ return {'format': 'vimeml_public_evaluation_v1', 'bundle_manifest_sha256': bundle_manifest_sha,
109
+ 'coreml_manifest_sha256': coreml_manifest_sha, 'training': {
110
+ 'prediction_targets': training['total_trained_tokens'], 'data_passes': training['completed_data_passes'],
111
+ 'full_validation': training['full_validation']['best'], 'test_used_for_selection': False},
112
+ 'ranking': results, 'alignment': {'strict_passed': alignment['passed'],
113
+ 'max_logit_abs': max(row['max_abs'] for row in alignment['logits']),
114
+ 'invariants': alignment['invariants']},
115
+ 'distribution': {'original_report': distribution,
116
+ 'aggregation': 'Mean of per-example position means; not corpus token-weighted.',
117
+ 'content_token_limit': 127, 'truncation_count_recorded': False,
118
+ 'original_report_has_full_identity_metadata': False},
119
+ 'source_reports_sha256': hashes,
120
+ 'scope': 'Offline fixed pools and saved Mac observations; not app-wide accuracy or a Windows Core ML runtime rerun.'}
121
+
122
+
123
+ def prepare(output, repo_id='Voltline/vimeml-tiny-ja-v1'):
124
+ output = Path(output).resolve()
125
+ if output.exists():
126
+ raise ValueError('Output exists; choose a new release version.')
127
+ if not re.fullmatch(r'[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+', repo_id):
128
+ raise ValueError('Use an OWNER/REPO model repository ID.')
129
+ if git('status', '--porcelain'):
130
+ raise ValueError('Commit the reviewed source on the current branch before preparing a release.')
131
+ from vimeml.deployment.bundle import verify_bundle
132
+ from vimeml.deployment.coreml import verify_package
133
+ check = verifier()
134
+ bundle = ROOT / 'artifacts/deployment/tiny-ja-v1-inference-v1'
135
+ model = ROOT / 'artifacts/deployment/tiny-ja-v1-conservative-int8-b32-v1'
136
+ metadata = verify_bundle(bundle)
137
+ coreml = verify_package(model)
138
+ bundle_sha, coreml_sha = check.sha(bundle / 'manifest.json'), check.sha(model / 'manifest.json')
139
+ if (metadata['parameter_count'] != 7386624 or coreml['kind'] != 'linear8_fp32_compute' or
140
+ coreml['minimum_ios'] != 18 or coreml['package_bytes'] != 8077801 or
141
+ coreml['bundle']['bundle_manifest_sha256'] != bundle_sha):
142
+ raise ValueError('Expected the reviewed frozen v1/INT8 package.')
143
+ summary = evaluation_summary(bundle_sha, coreml_sha)
144
+ for role, expected in [('dev', (137, 122, 134)), ('ajimee', (200, 124, 151))]:
145
+ for variant in ('fp32_lm', 'int8_lm'):
146
+ actual = summary['ranking'][role][variant]
147
+ if tuple(actual[key] for key in ('cases', 'top1_correct', 'top5_correct')) != expected:
148
+ raise ValueError('Saved metrics differ from this v1 model card; review before publishing.')
149
+ commit = git('rev-parse', 'HEAD')
150
+ output.mkdir(parents=True, exist_ok=False)
151
+ for name in [*metadata['files'], 'manifest.json']:
152
+ copy_checked(bundle / name, output / 'inference' / name)
153
+ for name, entry in coreml['package_files'].items():
154
+ copy_checked(model / 'model.mlpackage' / name,
155
+ output / 'coreml/ios18-int8-block32/model.mlpackage' / name, entry['sha256'])
156
+ copy_checked(model / 'manifest.json', output / 'coreml/ios18-int8-block32/manifest.json')
157
+ # Copy committed code/configuration, never data, annotations, artifacts or secrets.
158
+ source_files = subprocess.check_output(['git', 'ls-files', '-z', '--', 'src', 'scripts', 'configs',
159
+ 'templates/huggingface', 'requirements.txt', 'LICENSE'], cwd=ROOT).decode('utf-8').split('\0')
160
+ for name in filter(None, source_files):
161
+ copy_checked(ROOT / name, output / 'source' / name)
162
+ (output / 'source/README.md').write_text(
163
+ f'VimeML source/configuration from Git commit {commit}. GPL-2.0; see LICENSE.\n'
164
+ 'The FP32 loader checks the original model and deployment code fingerprints.\n'
165
+ 'Corpus texts, annotations, optimizer state and the separate Vime client are not included.\n', encoding='utf-8', newline='\n')
166
+ copy_checked(ROOT / 'LICENSE', output / 'LICENSE')
167
+ for name in ('infer.py', 'verify_release.py'):
168
+ copy_checked(ROOT / 'templates/huggingface' / name, output / name)
169
+ card = (ROOT / 'templates/huggingface/README.md').read_text(encoding='utf-8')
170
+ for key, value in {'SOURCE_COMMIT': commit, 'BUNDLE_SHA': bundle_sha, 'COREML_SHA': coreml_sha,
171
+ 'TOKENIZER_SHA': metadata['files']['tokenizer.model']['sha256'],
172
+ 'CHECKPOINT_SHA': metadata['checkpoint_sha256']}.items():
173
+ card = card.replace(f'@@{key}@@', value)
174
+ if '@@' in card:
175
+ raise ValueError('Unresolved model card field.')
176
+ (output / 'README.md').write_text(card, encoding='utf-8', newline='\n')
177
+ write_json(output / 'evaluation/summary.json', summary)
178
+ files = {p.relative_to(output).as_posix(): {'bytes': p.stat().st_size, 'sha256': check.sha(p)}
179
+ for p in sorted(output.rglob('*')) if p.is_file()}
180
+ release = {'format': 'vimeml_hf_release_v1', 'repo_id': repo_id, 'license': 'gpl-2.0',
181
+ 'source_repository': 'https://github.com/Voltline/VimeML', 'source_commit': commit,
182
+ 'created_utc': datetime.now(timezone.utc).isoformat(), 'bundle_manifest_sha256': bundle_sha,
183
+ 'coreml_manifest_sha256': coreml_sha, 'files': files,
184
+ 'policy': 'Local preparation only. No training, conversion, compression, device access or upload.'}
185
+ write_json(output / 'RELEASE.json', release)
186
+ sums = {**{name: entry['sha256'] for name, entry in files.items()}, 'RELEASE.json': check.sha(output / 'RELEASE.json')}
187
+ (output / 'SHA256SUMS.txt').write_text(''.join(f'{digest} {name}\n' for name, digest in sorted(sums.items())),
188
+ encoding='utf-8', newline='\n')
189
+ _, verified = check.verify(output)
190
+ print(json.dumps({'release': str(output), 'repo_id': repo_id, 'source_commit': commit,
191
+ 'verified_files': len(verified), 'bytes': sum((output / name).stat().st_size for name in verified)}, indent=2))
192
+ return release
193
+
194
+
195
+ def main():
196
+ parser = argparse.ArgumentParser(description=__doc__)
197
+ sub = parser.add_subparsers(dest='command', required=True)
198
+ build = sub.add_parser('prepare', help='Create a fresh local release from the committed source and frozen artifacts.')
199
+ build.add_argument('--output', type=Path, required=True)
200
+ build.add_argument('--repo-id', default='Voltline/vimeml-tiny-ja-v1')
201
+ verify = sub.add_parser('verify', help='Read-only checksum/inventory verification.')
202
+ verify.add_argument('--release', type=Path, required=True)
203
+ args = parser.parse_args()
204
+ if args.command == 'prepare':
205
+ prepare(args.output, args.repo_id)
206
+ else:
207
+ manifest, files = verifier().verify(args.release)
208
+ print(f"Verified {len(files)} release files for {manifest['repo_id']}")
209
+
210
+
211
+ if __name__ == '__main__':
212
+ main()