androiddrew commited on
Commit
a66fdc1
·
verified ·
1 Parent(s): feccf88

ONNX release v1

Browse files
Files changed (10) hide show
  1. LICENSE +201 -0
  2. NOTICE +15 -0
  3. README.md +92 -0
  4. config.json +58 -0
  5. conversion/README.md +65 -0
  6. conversion/export.py +398 -0
  7. conversion/pyproject.toml +25 -0
  8. conversion/uv.lock +247 -0
  9. decoder.onnx +3 -0
  10. encoder.onnx +3 -0
LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
NOTICE ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ g2p_en 2.1.0: English grapheme-to-phoneme conversion
2
+ Original work by Kyubyong Park and Jongseok Kim, licensed under the Apache
3
+ License, Version 2.0. https://github.com/Kyubyong/g2p
4
+ Source file: g2p_en/checkpoint20.npz (SHA-256
5
+ b8af35e4596d8dd5836dfd3fe9b2ba4f97b9c311efe8879544cbcfcbd566d8c6)
6
+
7
+ This repository contains an ONNX conversion made by Drew Bednar for
8
+ go-phonmatchnet, from the repackaging androiddrew/g2p-en-decoder-pytorch
9
+ (revision v1, commit fc55559c25a12fce6e0cf9062729e7f2a9fc1ddc). It is not
10
+ affiliated with or endorsed by the g2p_en authors.
11
+
12
+ Modifications to the original work: the NumPy arrays were renamed and stored
13
+ in safetensors format, then the network was split into a word-encoder graph
14
+ and a single-step decoder graph and exported to ONNX. Values are unchanged.
15
+ No training or fine-tuning was performed.
README.md ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ library_name: onnx
4
+ language:
5
+ - en
6
+ pipeline_tag: feature-extraction
7
+ tags:
8
+ - grapheme-to-phoneme
9
+ - g2p
10
+ - keyword-spotting
11
+ - wake-word
12
+ - onnx
13
+ base_model: androiddrew/g2p-en-decoder-pytorch
14
+ ---
15
+
16
+ # g2p_en decoder: keyword text features, ONNX
17
+
18
+ The [g2p_en](https://github.com/Kyubyong/g2p) grapheme-to-phoneme model
19
+ (Kyubyong Park & Jongseok Kim, Apache-2.0, release 2.1.0) as two ONNX graphs,
20
+ converted from
21
+ [`androiddrew/g2p-en-decoder-pytorch`](https://huggingface.co/androiddrew/g2p-en-decoder-pytorch).
22
+ The values are unchanged.
23
+
24
+ It is **not** used to output phoneme strings. The keyword model
25
+ [`androiddrew/naptrap-phonmatchnet-en-onnx`](https://huggingface.co/androiddrew/naptrap-phonmatchnet-en-onnx)
26
+ was trained on the decoder's **hidden states**. It is embedded in
27
+ [go-phonmatchnet](https://github.com/androiddrew/go-phonmatchnet).
28
+
29
+ > Not affiliated with or endorsed by the g2p_en authors. See `LICENSE` and `NOTICE`.
30
+
31
+ ## Files
32
+
33
+ | Graph | Inputs | Outputs |
34
+ |---|---|---|
35
+ | `encoder.onnx` | `graphemes` int64 `[L]`: a word's grapheme IDs, then `</s>` (2) | `hidden` `[1, 256]`; `separator` `[1, 256]`, the row placed between words |
36
+ | `decoder.onnx` | `token` int64 `[1]`, `hidden` `[1, 256]` | `next_hidden` `[1, 256]`; `logits` `[1, 74]` |
37
+
38
+ `config.json` lists the grapheme vocabulary, start/end phoneme IDs, the
39
+ 20-step limit and the SHA-256 of both graphs. `conversion/` holds the export
40
+ script, its locked environment and README.
41
+
42
+ ## Keyword features
43
+
44
+ 1. Normalize the keyword: NFKD, drop combining marks, lowercase, and keep
45
+ words matching `[a-z]+(?:'[a-z]+)*`. Numbers must be spelled out.
46
+ 2. Encode each word: grapheme IDs from `config.json` (`<unk>` for `'`), then `</s>`.
47
+ 3. Decode greedily from token 2 (`<s>`) for up to 20 steps; each step's
48
+ `next_hidden` is one feature row, until the argmax token is 3 (`</s>`),
49
+ whose state is not kept.
50
+ 4. Put the `separator` row between words.
51
+
52
+ ```python
53
+ import numpy as np
54
+ import onnxruntime as ort
55
+
56
+ encoder = ort.InferenceSession("encoder.onnx")
57
+ decoder = ort.InferenceSession("decoder.onnx")
58
+ graphemes = {g: i for i, g in enumerate(config["graphemes"])}
59
+
60
+ def features(normalized):
61
+ rows = []
62
+ for i, word in enumerate(normalized.split()):
63
+ ids = np.array([graphemes.get(c, 1) for c in word] + [2], dtype=np.int64)
64
+ hidden, separator = encoder.run(None, {"graphemes": ids})
65
+ if i:
66
+ rows.append(separator)
67
+ token = 2
68
+ for _ in range(20):
69
+ hidden, logits = decoder.run(None, {"token": np.array([token]), "hidden": hidden})
70
+ token = int(logits.argmax())
71
+ if token == 3:
72
+ break
73
+ rows.append(hidden)
74
+ return np.concatenate(rows) # float32 [positions, 256]
75
+ ```
76
+
77
+ ## Conversion and accuracy
78
+
79
+ `conversion/export.py` loads the PyTorch release at commit
80
+ `fc55559c25a12fce6e0cf9062729e7f2a9fc1ddc` with naptrap 0.1.0. The grapheme
81
+ GRU has PyTorch's gate layout, so the encoder is an ONNX `GRU` operator; the
82
+ decoder is one GRU-cell step plus the output projection. Over eight keywords,
83
+ including apostrophes, accents, one letter, a 34-letter word and a nine-word
84
+ sentence, every feature length matched naptrap and the maximum difference was
85
+ 5.5e-6. With the locked environment it reproduces both graphs byte for byte.
86
+
87
+ ## Limitations
88
+
89
+ The model was trained on CMUdict pronunciations of American English words.
90
+ Unusual names, brand names and non-English words get its best-guess
91
+ pronunciation, which may not match how people say them. The dictionary-lookup,
92
+ homograph and number-expansion steps of the g2p_en package are not used.
config.json ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "format": "naptrap-g2p-en-decoder-onnx-v1",
3
+ "source": {
4
+ "repo_id": "androiddrew/g2p-en-decoder-pytorch",
5
+ "commit": "fc55559c25a12fce6e0cf9062729e7f2a9fc1ddc",
6
+ "format": "naptrap-g2p-en-decoder-v1",
7
+ "sha256": "bba37553a3414f90c7ed0cd465925aa15def67f42cc59bf43b8c633e1eb7b445"
8
+ },
9
+ "opset": 17,
10
+ "hidden_size": 256,
11
+ "graphemes": [
12
+ "<pad>",
13
+ "<unk>",
14
+ "</s>",
15
+ "a",
16
+ "b",
17
+ "c",
18
+ "d",
19
+ "e",
20
+ "f",
21
+ "g",
22
+ "h",
23
+ "i",
24
+ "j",
25
+ "k",
26
+ "l",
27
+ "m",
28
+ "n",
29
+ "o",
30
+ "p",
31
+ "q",
32
+ "r",
33
+ "s",
34
+ "t",
35
+ "u",
36
+ "v",
37
+ "w",
38
+ "x",
39
+ "y",
40
+ "z"
41
+ ],
42
+ "phonemes": 74,
43
+ "start_token": 2,
44
+ "end_token": 3,
45
+ "max_decoder_steps": 20,
46
+ "inputs": {
47
+ "encoder": "graphemes [L] int64: a word's grapheme IDs followed by </s> (2)",
48
+ "decoder": "token [1] int64 (start with 2), hidden [1,256] float32"
49
+ },
50
+ "outputs": {
51
+ "encoder": "hidden [1,256]; separator [1,256], the row placed between words",
52
+ "decoder": "next_hidden [1,256], one feature row; logits [1,74], argmax is the next token"
53
+ },
54
+ "sha256": {
55
+ "encoder.onnx": "c492a8907a8b62d7f1e311c0df469490a4e4670865a6a5d8b4b7749a38666091",
56
+ "decoder.onnx": "86953a2604f3494bf44c4d81df67c444455b4a9f0cf6f08d62fab08393bd29e1"
57
+ }
58
+ }
conversion/README.md ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Export tooling
2
+
3
+ `export.py` rebuilds every model go-phonmatchnet embeds (`models/`) from
4
+ naptrap's PyTorch releases on Hugging Face, pinned by commit and SHA-256:
5
+
6
+ | Source | Commit | Output |
7
+ |---|---|---|
8
+ | `androiddrew/naptrap-phonmatchnet-en` v1 | `e8f6c45bff04d7368cbe14979392b7d52dc88ba8` | `phonmatchnet/model.onnx`, `config.json` |
9
+ | `androiddrew/google-speech-embedding-pytorch` v1 | `ad7a3dd93b02f83797a040857425d6343d98680a` | `google/model.onnx`, `config.json` |
10
+ | `androiddrew/g2p-en-decoder-pytorch` v1 | `fc55559c25a12fce6e0cf9062729e7f2a9fc1ddc` | `g2p/encoder.onnx`, `decoder.onnx`, `config.json` |
11
+
12
+ The PyTorch modules come from naptrap 0.1.0, so the graphs are traced from the
13
+ same code that defines the reference outputs. The trained values are copied
14
+ unchanged. Two things are re-expressed for ONNX:
15
+
16
+ - **Spectrogram frontends.** `torch.stft` and `torch.fft.rfft` do not export
17
+ reliably, so each FFT is a strided `Conv` with a fixed cos/sin DFT basis
18
+ that has the original window folded in. Reflect and zero padding, power or
19
+ magnitude, the mel matrix and the log are ordinary graph operators. The
20
+ graphs take raw samples.
21
+ - **The g2p greedy decode.** The encoder is an ONNX `GRU`; the decoder is one
22
+ step, which the caller runs until the end token. Data-dependent loops stay
23
+ out of the graph.
24
+
25
+ After exporting, the script runs every graph with ONNX Runtime against
26
+ naptrap's PyTorch modules (17 clips of speech and noise from 8,000 to
27
+ 32,000 samples long, eight keywords) and fails outside 1e-3 on embeddings and
28
+ logits or 1e-5 on text features. The measured maxima are about 4e-5, 1e-5 and 5.5e-6.
29
+
30
+ ```bash
31
+ cd tools/export
32
+ uv run --frozen python export.py --output out --check
33
+ ```
34
+
35
+ `--check` compares each output with the committed file and fails on any
36
+ difference. With the locked environment in `uv.lock` (Python 3.12, torch
37
+ 2.6.0+cpu, onnx 1.17.0, onnxruntime 1.22.1, naptrap 0.1.0) on Linux/amd64,
38
+ all seven outputs reproduce byte for byte. Copy `out/` over `models/` only
39
+ when intentionally changing the embedded models.
40
+
41
+ ## Hugging Face repositories
42
+
43
+ ```bash
44
+ uv run --frozen python hf.py --output hf-out
45
+ ```
46
+
47
+ assembles `hf-out/naptrap-phonmatchnet-en-onnx`, `google-speech-embedding-onnx`
48
+ and `g2p-en-decoder-onnx` from the committed `models/`, the cards and NOTICE
49
+ files in `hf/`, the Apache-2.0 LICENSE and this directory as `conversion/`.
50
+
51
+ ## Test fixtures
52
+
53
+ `fixtures.py` builds `testdata/`:
54
+
55
+ ```bash
56
+ uv run --frozen python fixtures.py synth --server http://host:8880 # Kokoro clips
57
+ uv run --frozen python fixtures.py reference # naptrap outputs
58
+ ```
59
+
60
+ `synth` requests each phrase from an OpenAI-compatible Kokoro speech server
61
+ (`kokoro-v1.0`; voices `af_heart`, `am_michael`, `bf_emma`), converts it to
62
+ 16 kHz 16-bit mono with ffmpeg and adds 0.2 s of silence on each side. TTS
63
+ output is not guaranteed to be bit-stable, so the clips are committed.
64
+ `reference` scores the committed clips with naptrap's PyTorch models and
65
+ writes `testdata/reference.json`.
conversion/export.py ADDED
@@ -0,0 +1,398 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Convert naptrap's PyTorch PhonMatchNet models to the ONNX graphs go-phonmatchnet embeds.
2
+
3
+ Sources are naptrap's three Hugging Face repositories at their v1 commits,
4
+ pinned by commit and SHA-256. The PyTorch modules come from naptrap 0.1.0, so
5
+ every graph is traced from the same code that produced the reference outputs.
6
+
7
+ Outputs, in the repository's models/ layout:
8
+
9
+ phonmatchnet/model.onnx waveform [N], google [G,96], text [P,256] -> logit [1]
10
+ google/model.onnx waveform [N] -> embeddings [floor(N/1280), 96]
11
+ g2p/encoder.onnx graphemes [L] -> hidden [1,256], separator [1,256]
12
+ g2p/decoder.onnx token [1], hidden [1,256] -> next_hidden [1,256], logits [1,74]
13
+ */config.json metadata the Go engine reads
14
+
15
+ The STFT frontends are inside the graphs. Each FFT is written as a strided
16
+ convolution with a fixed DFT basis (the window folded in), which ONNX exports
17
+ exactly; torch.stft and torch.fft do not export reliably. The g2p greedy
18
+ decode stays a loop in Go around the single-step decoder graph.
19
+
20
+ Usage: uv run --frozen python export.py --output out [--check]
21
+ """
22
+
23
+ import argparse
24
+ import hashlib
25
+ import json
26
+ import math
27
+ import shutil
28
+ import sys
29
+ import urllib.request
30
+ from pathlib import Path
31
+
32
+ import numpy as np
33
+ import onnx
34
+ import onnxruntime
35
+ import torch
36
+ from torch import nn
37
+ from torch.nn import functional as F
38
+
39
+ from naptrap.models.phonmatchnet import PhonMatchNet
40
+ from naptrap.text import GRAPHEMES
41
+
42
+ REPO = Path(__file__).resolve().parents[2]
43
+ OPSET = 17
44
+ HF = "https://huggingface.co/androiddrew/{repo}/resolve/{commit}/{file}"
45
+ SOURCES = {
46
+ "naptrap-phonmatchnet-en": ("e8f6c45bff04d7368cbe14979392b7d52dc88ba8", {
47
+ "config.json": "33b0a0decd5b563ebc67cd248008f8fcfcca8a2e71c03b79da9677470b7a619a",
48
+ "model.safetensors": "6cdb3f86b6ab57a00d47d3436b16c7ffd9c4b0d049cfcb98d49a478877213226",
49
+ "examples/said-mister.wav": "770afe00e123da53a8ef37be2b637083b2b84cdaeba59ab2d060878d349b2403",
50
+ }),
51
+ "google-speech-embedding-pytorch": ("ad7a3dd93b02f83797a040857425d6343d98680a", {
52
+ "config.json": "cd241bf118d3e16c74e3850ed6c75340216af00c8810835c95b8b620c6f22b6f",
53
+ "model.safetensors": "80a10015f4eb8ff9ba1aa10b56b1d6e27ef9f9f3419ebbae56fb43e9afce37be",
54
+ }),
55
+ "g2p-en-decoder-pytorch": ("fc55559c25a12fce6e0cf9062729e7f2a9fc1ddc", {
56
+ "config.json": "d1f02ff5209f688537731abdb849c5b793538abecdb5b9c19c1a0c0f25a03b11",
57
+ "model.safetensors": "bba37553a3414f90c7ed0cd465925aa15def67f42cc59bf43b8c633e1eb7b445",
58
+ }),
59
+ }
60
+ OUTPUTS = [
61
+ "phonmatchnet/model.onnx",
62
+ "phonmatchnet/config.json",
63
+ "google/model.onnx",
64
+ "google/config.json",
65
+ "g2p/encoder.onnx",
66
+ "g2p/decoder.onnx",
67
+ "g2p/config.json",
68
+ ]
69
+ # Clip lengths cover the accepted range, both frame-length rules and every
70
+ # boundary where N % 160 or N % 1280 changes the frame count.
71
+ LENGTHS = [8000, 8001, 8159, 8160, 12800, 12801, 16000, 23999, 31840, 32000]
72
+ KEYWORDS = ["hey computer", "said mister", "okay naptrap", "don't stop", "Café au lait", "a",
73
+ "supercalifragilisticexpialidocious", "the quick brown fox jumps over the lazy dog"]
74
+
75
+
76
+ def sha256(path):
77
+ return hashlib.sha256(Path(path).read_bytes()).hexdigest()
78
+
79
+
80
+ def fetch(cache):
81
+ """Download each pinned source file once and verify it."""
82
+ roots = {}
83
+ for repo, (commit, files) in SOURCES.items():
84
+ root = cache / repo / commit
85
+ for name, digest in files.items():
86
+ path = root / name
87
+ if not path.exists() or sha256(path) != digest:
88
+ path.parent.mkdir(parents=True, exist_ok=True)
89
+ url = HF.format(repo=repo, commit=commit, file=name)
90
+ print("download", url, file=sys.stderr)
91
+ with urllib.request.urlopen(url) as response:
92
+ path.write_bytes(response.read())
93
+ if sha256(path) != digest:
94
+ raise SystemExit(f"{repo}/{name}: SHA-256 mismatch")
95
+ roots[repo] = root
96
+ return roots
97
+
98
+
99
+ def dft_basis(window, n_fft, offset=0):
100
+ """[2*(n_fft//2+1), 1, taps] cos/-sin DFT rows with the window applied.
101
+
102
+ The window sits at ``offset`` inside the n_fft-point frame; taps after it
103
+ multiply zeros, so they are dropped.
104
+ """
105
+ bins = n_fft // 2 + 1
106
+ padded = torch.zeros(n_fft, dtype=torch.float64)
107
+ padded[offset:offset + window.numel()] = window.double()
108
+ n = torch.arange(n_fft, dtype=torch.float64)
109
+ angle = 2 * math.pi * torch.arange(bins, dtype=torch.float64)[:, None] * n[None, :] / n_fft
110
+ basis = torch.cat([torch.cos(angle), -torch.sin(angle)]) * padded
111
+ length = offset + window.numel()
112
+ return basis[:, :length].float().unsqueeze(1).contiguous()
113
+
114
+
115
+ class GoogleGraph(nn.Module):
116
+ """naptrap GoogleSpeechEmbedding with its rfft frontend as a DFT convolution."""
117
+
118
+ def __init__(self, google):
119
+ super().__init__()
120
+ self.google = google
121
+ self.register_buffer("basis", dft_basis(google.window, 512))
122
+
123
+ def forward(self, waveform):
124
+ padded = F.pad(waveform, (11120, 0)).reshape(1, 1, -1)
125
+ spectrum = F.conv1d(padded, self.basis, stride=160)[0]
126
+ real, imag = spectrum[:257], spectrum[257:]
127
+ magnitude = torch.sqrt(real * real + imag * imag).transpose(0, 1)
128
+ log_mel = torch.log(magnitude @ self.google.mel_matrix + .001)
129
+ return self.google.encode_log_mel(log_mel)
130
+
131
+
132
+ class PhonMatchNetGraph(nn.Module):
133
+ """naptrap PhonMatchNetwork with its centered STFT frontend as a DFT convolution."""
134
+
135
+ def __init__(self, network):
136
+ super().__init__()
137
+ self.network = network
138
+ frontend = network.audio.frontend
139
+ # torch.stft centers a 400-sample window inside the 512-point frame.
140
+ self.register_buffer("basis", dft_basis(frontend.window, 512, offset=56))
141
+ self.register_buffer("mel_matrix", frontend.mel_matrix.clone())
142
+
143
+ def forward(self, waveform, google, text):
144
+ samples = waveform.shape[0]
145
+ padded = F.pad(waveform.reshape(1, 1, -1), (256, 256), mode="reflect")
146
+ spectrum = F.conv1d(padded, self.basis, stride=160)[0]
147
+ real, imag = spectrum[:257], spectrum[257:]
148
+ power = (real * real + imag * imag).transpose(0, 1)
149
+ keyword_mel = torch.log10(power @ self.mel_matrix + 1e-7)
150
+ keyword_mel = keyword_mel[:(samples + 159) // 160].unsqueeze(0)
151
+ return self.network(keyword_mel, google.unsqueeze(0), text.unsqueeze(0))
152
+
153
+
154
+ class G2PEncoderGraph(nn.Module):
155
+ """Grapheme IDs of one word (ending in </s>) -> final encoder GRU state.
156
+
157
+ The g2p_en cell has PyTorch's GRU gate layout (r, z, n with the reset gate
158
+ applied after the hidden projection), so nn.GRU exports it as an ONNX GRU.
159
+ The decoder's <pad> phoneme embedding is returned as the word separator.
160
+ """
161
+
162
+ def __init__(self, text):
163
+ super().__init__()
164
+ size = text.size
165
+ self.embedding = nn.Parameter(text.enc_emb.detach().clone())
166
+ self.gru = nn.GRU(size, size, batch_first=True)
167
+ cell = text.encoder
168
+ self.gru.weight_ih_l0.data.copy_(cell.w_ih)
169
+ self.gru.weight_hh_l0.data.copy_(cell.w_hh)
170
+ self.gru.bias_ih_l0.data.copy_(cell.b_ih)
171
+ self.gru.bias_hh_l0.data.copy_(cell.b_hh)
172
+ self.register_buffer("pad", text.dec_emb[0:1].detach().clone())
173
+
174
+ def forward(self, graphemes):
175
+ _, hidden = self.gru(self.embedding[graphemes].unsqueeze(0))
176
+ return hidden[0], self.pad
177
+
178
+
179
+ class G2PDecoderGraph(nn.Module):
180
+ """One greedy decoder step: previous phoneme ID and state -> new state, logits."""
181
+
182
+ def __init__(self, text):
183
+ super().__init__()
184
+ self.text = text
185
+
186
+ def forward(self, token, hidden):
187
+ hidden = self.text.decoder(self.text.dec_emb[token], hidden)
188
+ return hidden, hidden @ self.text.fc_w.T + self.text.fc_b
189
+
190
+
191
+ def export(module, path, args, inputs, outputs, axes):
192
+ path.parent.mkdir(parents=True, exist_ok=True)
193
+ module.eval()
194
+ with torch.no_grad():
195
+ torch.onnx.export(module, args, str(path), input_names=inputs, output_names=outputs,
196
+ dynamic_axes=axes, opset_version=OPSET, do_constant_folding=True, dynamo=False)
197
+ model = onnx.load(str(path))
198
+ # A constant output (the g2p separator) is folded into an initializer and
199
+ # loses its requested name; restore it with an Identity node.
200
+ for output, name in zip(model.graph.output, outputs):
201
+ if output.name != name:
202
+ model.graph.node.append(onnx.helper.make_node("Identity", [output.name], [name]))
203
+ output.name = name
204
+ onnx.checker.check_model(model, full_check=True)
205
+ # Drop the exporter's version stamp so the bytes depend only on the graph.
206
+ model.producer_version = ""
207
+ onnx.save(model, str(path))
208
+
209
+
210
+ def companion_config(source_config, repo, commit, fmt, files, extra):
211
+ config = {
212
+ "format": fmt,
213
+ "source": {
214
+ "repo_id": "androiddrew/" + repo,
215
+ "commit": commit,
216
+ "format": source_config["format"],
217
+ "sha256": source_config["sha256"]["model.safetensors"],
218
+ },
219
+ "opset": OPSET,
220
+ }
221
+ config.update(extra)
222
+ config["sha256"] = {name: sha256(path) for name, path in files}
223
+ return config
224
+
225
+
226
+ def write_json(path, value):
227
+ path.write_text(json.dumps(value, indent=2) + "\n")
228
+
229
+
230
+ def build(roots, output):
231
+ pm_root = roots["naptrap-phonmatchnet-en"]
232
+ google_root = roots["google-speech-embedding-pytorch"]
233
+ g2p_root = roots["g2p-en-decoder-pytorch"]
234
+ model = PhonMatchNet(pm_root, google_root, g2p_root)
235
+ google, text = model.google_encoder, model.text_encoder
236
+ configs = {key: json.loads((roots[repo] / "config.json").read_text()) for key, repo in [
237
+ ("phonmatchnet", "naptrap-phonmatchnet-en"), ("google", "google-speech-embedding-pytorch"),
238
+ ("g2p", "g2p-en-decoder-pytorch")]}
239
+
240
+ wave = torch.zeros(16000)
241
+ export(GoogleGraph(google), output / "google/model.onnx", (wave,), ["waveform"], ["embeddings"],
242
+ {"waveform": {0: "samples"}, "embeddings": {0: "frames"}})
243
+ export(PhonMatchNetGraph(model._model), output / "phonmatchnet/model.onnx",
244
+ (wave, torch.zeros(12, 96), torch.zeros(9, 256)), ["waveform", "google", "text"], ["logit"],
245
+ {"waveform": {0: "samples"}, "google": {0: "google_frames"}, "text": {0: "positions"}})
246
+ export(G2PEncoderGraph(text), output / "g2p/encoder.onnx", (torch.tensor([5, 6, 2]),),
247
+ ["graphemes"], ["hidden", "separator"], {"graphemes": {0: "length"}})
248
+ export(G2PDecoderGraph(text), output / "g2p/decoder.onnx", (torch.tensor([2]), torch.zeros(1, 256)),
249
+ ["token", "hidden"], ["next_hidden", "logits"], {})
250
+
251
+ source = configs["phonmatchnet"]
252
+ commit = SOURCES["naptrap-phonmatchnet-en"][0]
253
+ write_json(output / "phonmatchnet/config.json", companion_config(
254
+ source, "naptrap-phonmatchnet-en", commit, "naptrap-phonmatchnet-onnx-v1",
255
+ [("model.onnx", output / "phonmatchnet/model.onnx")], {
256
+ "architecture": source["architecture"],
257
+ "task": source["task"],
258
+ "language": source["language"],
259
+ "thresholds": source["thresholds"],
260
+ "threshold_note": source["threshold_note"],
261
+ "inputs": {
262
+ "waveform": "[N] mono 16 kHz float32 in [-1, 1], 8000 <= N <= 32000",
263
+ "google": "[floor(N / 1280), 96] output of google/model.onnx for the same waveform",
264
+ "text": "[P, 256] g2p decoder features, 1 <= P <= 128",
265
+ },
266
+ "outputs": {"logit": "[1] keyword logit; score = sigmoid(logit)"},
267
+ "frontend": source["frontend"],
268
+ # The companion graphs this model was trained with, pinned like the
269
+ # PyTorch release pins its encoders.
270
+ "requires": {
271
+ "google": {"repo_id": "androiddrew/google-speech-embedding-onnx", "revision": "v1",
272
+ "sha256": {"model.onnx": sha256(output / "google/model.onnx")}},
273
+ "g2p": {"repo_id": "androiddrew/g2p-en-decoder-onnx", "revision": "v1",
274
+ "sha256": {"encoder.onnx": sha256(output / "g2p/encoder.onnx"),
275
+ "decoder.onnx": sha256(output / "g2p/decoder.onnx")}},
276
+ },
277
+ }))
278
+ source = configs["google"]
279
+ write_json(output / "google/config.json", companion_config(
280
+ source, "google-speech-embedding-pytorch", SOURCES["google-speech-embedding-pytorch"][0],
281
+ "naptrap-google-speech-embedding-onnx-v1", [("model.onnx", output / "google/model.onnx")], {
282
+ "embedding_size": source["embedding_size"],
283
+ "inputs": {"waveform": "[N] mono 16 kHz float32, N >= 1280"},
284
+ "outputs": {"embeddings": "[floor(N / 1280), 96]"},
285
+ "preprocessing": source["preprocessing"],
286
+ }))
287
+ source = configs["g2p"]
288
+ write_json(output / "g2p/config.json", companion_config(
289
+ source, "g2p-en-decoder-pytorch", SOURCES["g2p-en-decoder-pytorch"][0],
290
+ "naptrap-g2p-en-decoder-onnx-v1", [("encoder.onnx", output / "g2p/encoder.onnx"),
291
+ ("decoder.onnx", output / "g2p/decoder.onnx")], {
292
+ "hidden_size": source["hidden_size"],
293
+ "graphemes": GRAPHEMES,
294
+ "phonemes": source["phonemes"],
295
+ "start_token": 2,
296
+ "end_token": 3,
297
+ "max_decoder_steps": source["max_decoder_steps"],
298
+ "inputs": {
299
+ "encoder": "graphemes [L] int64: a word's grapheme IDs followed by </s> (2)",
300
+ "decoder": "token [1] int64 (start with 2), hidden [1,256] float32",
301
+ },
302
+ "outputs": {
303
+ "encoder": "hidden [1,256]; separator [1,256], the row placed between words",
304
+ "decoder": "next_hidden [1,256], one feature row; logits [1,74], argmax is the next token",
305
+ },
306
+ }))
307
+ return model
308
+
309
+
310
+ class OnnxText:
311
+ """The Go engine's keyword algorithm, run with ONNX Runtime."""
312
+
313
+ def __init__(self, output):
314
+ self.encoder = onnxruntime.InferenceSession(str(output / "g2p/encoder.onnx"))
315
+ self.decoder = onnxruntime.InferenceSession(str(output / "g2p/decoder.onnx"))
316
+ self.index = {g: i for i, g in enumerate(GRAPHEMES)}
317
+
318
+ def __call__(self, normalized):
319
+ rows = []
320
+ for i, word in enumerate(normalized.split()):
321
+ ids = np.array([self.index.get(c, 1) for c in word] + [2], dtype=np.int64)
322
+ hidden, separator = self.encoder.run(None, {"graphemes": ids})
323
+ if i:
324
+ rows.append(separator)
325
+ token = 2
326
+ for _ in range(20):
327
+ hidden, logits = self.decoder.run(None, {"token": np.array([token], dtype=np.int64),
328
+ "hidden": hidden})
329
+ token = int(logits.argmax())
330
+ if token == 3:
331
+ break
332
+ rows.append(hidden)
333
+ return np.concatenate(rows)
334
+
335
+
336
+ def verify(model, output, wav):
337
+ """Compare every graph with naptrap's PyTorch modules; fail outside tolerance."""
338
+ google_session = onnxruntime.InferenceSession(str(output / "google/model.onnx"))
339
+ pm_session = onnxruntime.InferenceSession(str(output / "phonmatchnet/model.onnx"))
340
+ text = OnnxText(output)
341
+ generator = torch.Generator().manual_seed(7)
342
+ clips = [("said-mister", wav)]
343
+ for n in LENGTHS:
344
+ noise = torch.randn(n, generator=generator) * 0.1
345
+ clips.append((f"noise-{n}", noise.clamp(-1, 1)))
346
+ if n >= wav.numel():
347
+ clips.append((f"said-mister-pad-{n}", F.pad(wav, (0, n - wav.numel()))))
348
+ worst = {"google": 0.0, "text": 0.0, "logit": 0.0}
349
+ keywords = {}
350
+ for keyword in KEYWORDS:
351
+ expected = model.keyword(keyword)
352
+ actual = text(expected.text)
353
+ if actual.shape != tuple(expected.features.shape):
354
+ raise SystemExit(f"text positions differ for {keyword!r}: {actual.shape} vs {tuple(expected.features.shape)}")
355
+ worst["text"] = max(worst["text"], float(np.abs(actual - expected.features.numpy()).max()))
356
+ keywords[keyword] = (expected, actual)
357
+ for name, clip in clips:
358
+ clip = clip.float().contiguous()
359
+ g_ref = model.google_encoder(clip)
360
+ (g_onnx,) = google_session.run(None, {"waveform": clip.numpy()})
361
+ worst["google"] = max(worst["google"], float(np.abs(g_onnx - g_ref.numpy()).max()))
362
+ mel_ref = model._frontend(clip)[0]
363
+ for keyword, (expected, features) in keywords.items():
364
+ ref = model.predict({"keyword_mel": mel_ref, "google": g_ref, "text": expected.features})
365
+ (logit,) = pm_session.run(None, {"waveform": clip.numpy(), "google": g_onnx, "text": features})
366
+ worst["logit"] = max(worst["logit"], abs(float(logit[0]) - ref.details["keyword_logit"]))
367
+ limits = {"google": 1e-3, "text": 1e-5, "logit": 1e-3}
368
+ for key, value in worst.items():
369
+ print(f"max |onnx - torch| {key}: {value:.3g} (limit {limits[key]:g})")
370
+ failed = [key for key, value in worst.items() if value > limits[key]]
371
+ if failed:
372
+ raise SystemExit("ONNX graphs differ from naptrap: " + ", ".join(failed))
373
+
374
+
375
+ def main():
376
+ parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
377
+ parser.add_argument("--output", type=Path, required=True)
378
+ parser.add_argument("--cache", type=Path, default=Path(__file__).resolve().parent / ".cache")
379
+ parser.add_argument("--check", action="store_true", help="compare outputs with the committed models/")
380
+ args = parser.parse_args()
381
+ torch.manual_seed(0)
382
+ roots = fetch(args.cache)
383
+ if args.output.exists():
384
+ shutil.rmtree(args.output)
385
+ model = build(roots, args.output)
386
+ from naptrap import load_wav
387
+ verify(model, args.output, load_wav(roots["naptrap-phonmatchnet-en"] / "examples/said-mister.wav"))
388
+ if args.check:
389
+ changed = [name for name in OUTPUTS
390
+ if not (REPO / "models" / name).exists()
391
+ or sha256(args.output / name) != sha256(REPO / "models" / name)]
392
+ if changed:
393
+ raise SystemExit("differs from models/: " + ", ".join(changed))
394
+ print(f"all {len(OUTPUTS)} outputs match models/")
395
+
396
+
397
+ if __name__ == "__main__":
398
+ main()
conversion/pyproject.toml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "go-phonmatchnet-export"
3
+ version = "0.1.0"
4
+ description = "Convert naptrap's PyTorch PhonMatchNet models to the ONNX graphs go-phonmatchnet embeds"
5
+ requires-python = "==3.12.*"
6
+ dependencies = [
7
+ "naptrap==0.1.0",
8
+ "numpy==2.2.6",
9
+ "onnx==1.17.0",
10
+ "onnxruntime==1.22.1",
11
+ "safetensors==0.5.3",
12
+ "torch==2.6.0+cpu",
13
+ ]
14
+
15
+ [tool.uv]
16
+ package = false
17
+ environments = ["sys_platform == 'linux' and platform_machine == 'x86_64'"]
18
+
19
+ [tool.uv.sources]
20
+ torch = { index = "pytorch-cpu" }
21
+
22
+ [[tool.uv.index]]
23
+ name = "pytorch-cpu"
24
+ url = "https://download.pytorch.org/whl/cpu"
25
+ explicit = true
conversion/uv.lock ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version = 1
2
+ revision = 3
3
+ requires-python = "==3.12.*"
4
+ resolution-markers = [
5
+ "platform_machine == 'x86_64' and sys_platform == 'linux'",
6
+ ]
7
+ supported-markers = [
8
+ "platform_machine == 'x86_64' and sys_platform == 'linux'",
9
+ ]
10
+
11
+ [[package]]
12
+ name = "coloredlogs"
13
+ version = "15.0.1"
14
+ source = { registry = "https://pypi.org/simple" }
15
+ dependencies = [
16
+ { name = "humanfriendly", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
17
+ ]
18
+ sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/eed8f27100517e8c0e6b923d5f0845d0cb99763da6fdee00478f91db7325/coloredlogs-15.0.1.tar.gz", hash = "sha256:7c991aa71a4577af2f82600d8f8f3a89f936baeaf9b50a9c197da014e5bf16b0", size = 278520, upload-time = "2021-06-11T10:22:45.202Z" }
19
+ wheels = [
20
+ { url = "https://files.pythonhosted.org/packages/a7/06/3d6badcf13db419e25b07041d9c7b4a2c331d3f4e7134445ec5df57714cd/coloredlogs-15.0.1-py2.py3-none-any.whl", hash = "sha256:612ee75c546f53e92e70049c9dbfcc18c935a2b9a53b66085ce9ef6a6e5c0934", size = 46018, upload-time = "2021-06-11T10:22:42.561Z" },
21
+ ]
22
+
23
+ [[package]]
24
+ name = "filelock"
25
+ version = "4.1.1"
26
+ source = { registry = "https://pypi.org/simple" }
27
+ sdist = { url = "https://files.pythonhosted.org/packages/35/c8/1d457d9150ff948f2ce6ada7715e0eeebbe5d3b58a45271a1e222474bcd3/filelock-4.1.1.tar.gz", hash = "sha256:7ba0927482c5a814b0a7f391d029ccdb8010f576f0a74c0dcde1811e8bc4c1b6", size = 563430, upload-time = "2026-10-11T16:11:54.373Z" }
28
+ wheels = [
29
+ { url = "https://files.pythonhosted.org/packages/d7/8b/f837f52905395ba4510fe61f753c24833fb0a9c76e21267bb9f828b664a9/filelock-4.1.1-py3-none-any.whl", hash = "sha256:3f4a557945a7b0f95efeb1f432267affe5d45ac8ddde2aed1b97ebb62382c089", size = 132460, upload-time = "2026-10-11T16:11:52.753Z" },
30
+ ]
31
+
32
+ [[package]]
33
+ name = "flatbuffers"
34
+ version = "25.12.19"
35
+ source = { registry = "https://pypi.org/simple" }
36
+ wheels = [
37
+ { url = "https://files.pythonhosted.org/packages/e8/2d/d2a548598be01649e2d46231d151a6c56d10b964d94043a335ae56ea2d92/flatbuffers-25.12.19-py2.py3-none-any.whl", hash = "sha256:7634f50c427838bb021c2d66a3d1168e9d199b0607e6329399f04846d42e20b4", size = 26661, upload-time = "2025-12-19T23:16:13.622Z" },
38
+ ]
39
+
40
+ [[package]]
41
+ name = "fsspec"
42
+ version = "2026.9.0"
43
+ source = { registry = "https://pypi.org/simple" }
44
+ sdist = { url = "https://files.pythonhosted.org/packages/77/cd/9be253869fc42e764de7f3dedd6969af7d44ff9c3375214a3442a6f3fc08/fsspec-2026.9.0.tar.gz", hash = "sha256:0f08147951c8cb31d844c3547d631053b127863b60be04cf06e121333ee0e2fe", size = 333545, upload-time = "2026-09-18T17:50:42.825Z" }
45
+ wheels = [
46
+ { url = "https://files.pythonhosted.org/packages/6c/c0/a98505f18594f1bce828bb159cec0fcf9860562f1a2c85913409fc8f3d9e/fsspec-2026.9.0-py3-none-any.whl", hash = "sha256:8dd6e646e99ea382bd85f97a45e6b526a442d79423a7dc673f1e2756d05fcb5f", size = 221738, upload-time = "2026-09-18T17:50:41.341Z" },
47
+ ]
48
+
49
+ [[package]]
50
+ name = "go-phonmatchnet-export"
51
+ version = "0.1.0"
52
+ source = { virtual = "." }
53
+ dependencies = [
54
+ { name = "naptrap", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
55
+ { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
56
+ { name = "onnx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
57
+ { name = "onnxruntime", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
58
+ { name = "safetensors", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
59
+ { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
60
+ ]
61
+
62
+ [package.metadata]
63
+ requires-dist = [
64
+ { name = "naptrap", specifier = "==0.1.0" },
65
+ { name = "numpy", specifier = "==2.2.6" },
66
+ { name = "onnx", specifier = "==1.17.0" },
67
+ { name = "onnxruntime", specifier = "==1.22.1" },
68
+ { name = "safetensors", specifier = "==0.5.3" },
69
+ { name = "torch", specifier = "==2.6.0+cpu", index = "https://download.pytorch.org/whl/cpu" },
70
+ ]
71
+
72
+ [[package]]
73
+ name = "humanfriendly"
74
+ version = "10.0"
75
+ source = { registry = "https://pypi.org/simple" }
76
+ sdist = { url = "https://files.pythonhosted.org/packages/cc/3f/2c29224acb2e2df4d2046e4c73ee2662023c58ff5b113c4c1adac0886c43/humanfriendly-10.0.tar.gz", hash = "sha256:6b0b831ce8f15f7300721aa49829fc4e83921a9a301cc7f606be6686a2288ddc", size = 360702, upload-time = "2021-09-17T21:40:43.31Z" }
77
+ wheels = [
78
+ { url = "https://files.pythonhosted.org/packages/f0/0f/310fb31e39e2d734ccaa2c0fb981ee41f7bd5056ce9bc29b2248bd569169/humanfriendly-10.0-py2.py3-none-any.whl", hash = "sha256:1697e1a8a8f550fd43c2865cd84542fc175a61dcb779b6fee18cf6b6ccba1477", size = 86794, upload-time = "2021-09-17T21:40:39.897Z" },
79
+ ]
80
+
81
+ [[package]]
82
+ name = "jinja2"
83
+ version = "3.1.6"
84
+ source = { registry = "https://pypi.org/simple" }
85
+ dependencies = [
86
+ { name = "markupsafe", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
87
+ ]
88
+ sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", hash = "sha256:0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d", size = 245115, upload-time = "2025-03-05T20:05:02.478Z" }
89
+ wheels = [
90
+ { url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", hash = "sha256:85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67", size = 134899, upload-time = "2025-03-05T20:05:00.369Z" },
91
+ ]
92
+
93
+ [[package]]
94
+ name = "markupsafe"
95
+ version = "3.0.4"
96
+ source = { registry = "https://pypi.org/simple" }
97
+ sdist = { url = "https://files.pythonhosted.org/packages/38/9b/e422a865e1d5d57d0e509b4e0bf1c1a70a7f6382c29a5aa428df994c8bc8/markupsafe-3.0.4.tar.gz", hash = "sha256:2e9ad7dd851bf45fab9f75cbff4cb493fee9979e8d8c7c9c3ee119022518edd6", size = 153777, upload-time = "2026-10-02T23:07:22.29Z" }
98
+ wheels = [
99
+ { url = "https://files.pythonhosted.org/packages/4f/a7/aeedb5140afa41fc74c225e9184ab96723a6e873b6ee1c9fede7283456d8/markupsafe-3.0.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8e124f974786f831d6043728e38296969d3579db8896fe004682f5758e613581", size = 22985, upload-time = "2026-10-02T23:04:57.521Z" },
100
+ { url = "https://files.pythonhosted.org/packages/0c/fe/fb1e79be0fea60aa32602ebefc9c35a82bb42b4df157285ab7dfec12341a/markupsafe-3.0.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c9a7f43c0b202b334cc9184af09bb8f21d3a209e038efaf106936fb69e6b026e", size = 23048, upload-time = "2026-10-02T23:05:04.479Z" },
101
+ ]
102
+
103
+ [[package]]
104
+ name = "mpmath"
105
+ version = "1.3.0"
106
+ source = { registry = "https://pypi.org/simple" }
107
+ sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
108
+ wheels = [
109
+ { url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
110
+ ]
111
+
112
+ [[package]]
113
+ name = "naptrap"
114
+ version = "0.1.0"
115
+ source = { registry = "https://pypi.org/simple" }
116
+ dependencies = [
117
+ { name = "safetensors", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
118
+ { name = "torch", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
119
+ ]
120
+ sdist = { url = "https://files.pythonhosted.org/packages/73/02/f32e8515c47f30688f191f35e594b74d1a70e176de57addddff950f73f3b/naptrap-0.1.0.tar.gz", hash = "sha256:d458c0999441710ea3d67622f7ef3c782c75a07624d44ecaacedfb93685ccb6d", size = 28457, upload-time = "2026-10-12T13:40:05.714Z" }
121
+ wheels = [
122
+ { url = "https://files.pythonhosted.org/packages/d3/70/7c0d678b0e60eaad6712685172a4b943a1d37ba50b2e6bcc39de870329aa/naptrap-0.1.0-py3-none-any.whl", hash = "sha256:8dc2a77ff212a40d7fe4b57bb79961870c16a3b123571a87c346ca96cfc63e52", size = 25185, upload-time = "2026-10-12T13:40:04.606Z" },
123
+ ]
124
+
125
+ [[package]]
126
+ name = "networkx"
127
+ version = "3.7"
128
+ source = { registry = "https://pypi.org/simple" }
129
+ sdist = { url = "https://files.pythonhosted.org/packages/dc/76/3af777226b63a5e64a6b36b1ec5855c14e2b94a37096d4760e595fc43511/networkx-3.7.tar.gz", hash = "sha256:fd77a511bd90f39f3d016351345b52cf5319b813bdca01de3f755d3cca62e96a", size = 1866482, upload-time = "2026-09-21T16:45:16.974Z" }
130
+ wheels = [
131
+ { url = "https://files.pythonhosted.org/packages/7e/cd/fe58041e9011f307c490e3e17dd48cc516448f7c698a3f2d9d9d65d7e6a8/networkx-3.7-py3-none-any.whl", hash = "sha256:e3fd2c13a7814cee3746340d8d7f8598a67f16a58bf47fb7f8793fab6efca1b0", size = 2142205, upload-time = "2026-09-21T16:45:14.609Z" },
132
+ ]
133
+
134
+ [[package]]
135
+ name = "numpy"
136
+ version = "2.2.6"
137
+ source = { registry = "https://pypi.org/simple" }
138
+ sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" }
139
+ wheels = [
140
+ { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" },
141
+ { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" },
142
+ ]
143
+
144
+ [[package]]
145
+ name = "onnx"
146
+ version = "1.17.0"
147
+ source = { registry = "https://pypi.org/simple" }
148
+ dependencies = [
149
+ { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
150
+ { name = "protobuf", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
151
+ ]
152
+ sdist = { url = "https://files.pythonhosted.org/packages/9a/54/0e385c26bf230d223810a9c7d06628d954008a5e5e4b73ee26ef02327282/onnx-1.17.0.tar.gz", hash = "sha256:48ca1a91ff73c1d5e3ea2eef20ae5d0e709bb8a2355ed798ffc2169753013fd3", size = 12165120, upload-time = "2024-10-01T21:48:40.63Z" }
153
+ wheels = [
154
+ { url = "https://files.pythonhosted.org/packages/3d/7c/67f4952d1b56b3f74a154b97d0dd0630d525923b354db117d04823b8b49b/onnx-1.17.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f3fb5cc4e2898ac5312a7dc03a65133dd2abf9a5e520e69afb880a7251ec97a", size = 16046307, upload-time = "2024-10-01T21:46:21.186Z" },
155
+ ]
156
+
157
+ [[package]]
158
+ name = "onnxruntime"
159
+ version = "1.22.1"
160
+ source = { registry = "https://pypi.org/simple" }
161
+ dependencies = [
162
+ { name = "coloredlogs", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
163
+ { name = "flatbuffers", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
164
+ { name = "numpy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
165
+ { name = "packaging", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
166
+ { name = "protobuf", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
167
+ { name = "sympy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
168
+ ]
169
+ wheels = [
170
+ { url = "https://files.pythonhosted.org/packages/0a/50/519828a5292a6ccd8d5cd6d2f72c6b36ea528a2ef68eca69647732539ffa/onnxruntime-1.22.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2d39a530aff1ec8d02e365f35e503193991417788641b184f5b1e8c9a6d5ce8d", size = 16475713, upload-time = "2025-07-10T19:15:45.452Z" },
171
+ ]
172
+
173
+ [[package]]
174
+ name = "packaging"
175
+ version = "26.3"
176
+ source = { registry = "https://pypi.org/simple" }
177
+ sdist = { url = "https://files.pythonhosted.org/packages/7d/fa/3944b40b07da9ce895c0e6303a5ab7d53da063554f534556b134a54d6093/packaging-26.3.tar.gz", hash = "sha256:94edc256424af38762eb31306eed28beb9f0efc50a8837492c9d6fd6004aed79", size = 313412, upload-time = "2026-08-04T18:15:28.737Z" }
178
+ wheels = [
179
+ { url = "https://files.pythonhosted.org/packages/63/34/ba1c580383c9eada3711951fef0795c80b829a078d72188184bcab9dd527/packaging-26.3-py3-none-any.whl", hash = "sha256:d7193f7c8e4e93f444fde0262bf90af30e16fa0ad0ad44cb553c87339b23cd1c", size = 129956, upload-time = "2026-08-04T18:15:27.159Z" },
180
+ ]
181
+
182
+ [[package]]
183
+ name = "protobuf"
184
+ version = "7.36.2"
185
+ source = { registry = "https://pypi.org/simple" }
186
+ sdist = { url = "https://files.pythonhosted.org/packages/d9/89/5b8517baa72f84a67b8a307ba953c91057af618bf40bf676f3c03551f8f0/protobuf-7.36.2.tar.gz", hash = "sha256:497d0463ff3316681da6c0b9e8d06cb465d61abce00b613ab42226175644d1bb", size = 512737, upload-time = "2026-09-17T20:07:59.326Z" }
187
+ wheels = [
188
+ { url = "https://files.pythonhosted.org/packages/db/f3/3996583dd2906297a637af12114deddf7658af6e683fedb83be061983fb5/protobuf-7.36.2-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:89f23aa53c24553a2416fd4fd1ec06f74fa42b14b546d8883128813f775bbfd2", size = 343223, upload-time = "2026-09-17T20:07:54.931Z" },
189
+ { url = "https://files.pythonhosted.org/packages/e4/04/d52c7016b04b6c5108f26691f9d33ec82a9b65d041f1a9c771137693d618/protobuf-7.36.2-py3-none-any.whl", hash = "sha256:bdb3a345d48db958e6ce1f18e508beb0cc981d64f24088427549c866cd039f1e", size = 179806, upload-time = "2026-09-17T20:07:58.211Z" },
190
+ ]
191
+
192
+ [[package]]
193
+ name = "safetensors"
194
+ version = "0.5.3"
195
+ source = { registry = "https://pypi.org/simple" }
196
+ sdist = { url = "https://files.pythonhosted.org/packages/71/7e/2d5d6ee7b40c0682315367ec7475693d110f512922d582fef1bd4a63adc3/safetensors-0.5.3.tar.gz", hash = "sha256:b6b0d6ecacec39a4fdd99cc19f4576f5219ce858e6fd8dbe7609df0b8dc56965", size = 67210, upload-time = "2025-02-26T09:15:13.155Z" }
197
+ wheels = [
198
+ { url = "https://files.pythonhosted.org/packages/a6/f8/dae3421624fcc87a89d42e1898a798bc7ff72c61f38973a65d60df8f124c/safetensors-0.5.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cead1fa41fc54b1e61089fa57452e8834f798cb1dc7a09ba3524f1eb08e0317a", size = 471642, upload-time = "2025-02-26T09:15:00.544Z" },
199
+ { url = "https://files.pythonhosted.org/packages/40/ad/2b113098e69c985a3d8fbda4b902778eae4a35b7d5188859b4a63d30c161/safetensors-0.5.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:37f1521be045e56fc2b54c606d4455573e717b2d887c579ee1dbba5f868ece04", size = 643147, upload-time = "2025-02-26T09:15:11.185Z" },
200
+ ]
201
+
202
+ [[package]]
203
+ name = "setuptools"
204
+ version = "84.0.0"
205
+ source = { registry = "https://pypi.org/simple" }
206
+ sdist = { url = "https://files.pythonhosted.org/packages/6d/44/f5da03a8ef95d369145c5bb53050e7877c9f3d312e128605fd9504829143/setuptools-84.0.0.tar.gz", hash = "sha256:f4695c21257f0d9b537ec2692c941d02ee143b7cc1276941349a546573b2ef73", size = 1168449, upload-time = "2026-08-08T18:27:58.365Z" }
207
+ wheels = [
208
+ { url = "https://files.pythonhosted.org/packages/95/9c/c510029fc6ef33a6275cd2c5d3cecd6613dfd6aa401d57c54f1c18852ccf/setuptools-84.0.0-py3-none-any.whl", hash = "sha256:51a52592b3b99e102b609654876bd65f19f999935166d1352678931132b0c670", size = 818216, upload-time = "2026-08-08T18:27:56.719Z" },
209
+ ]
210
+
211
+ [[package]]
212
+ name = "sympy"
213
+ version = "1.13.1"
214
+ source = { registry = "https://pypi.org/simple" }
215
+ dependencies = [
216
+ { name = "mpmath", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
217
+ ]
218
+ sdist = { url = "https://files.pythonhosted.org/packages/ca/99/5a5b6f19ff9f083671ddf7b9632028436167cd3d33e11015754e41b249a4/sympy-1.13.1.tar.gz", hash = "sha256:9cebf7e04ff162015ce31c9c6c9144daa34a93bd082f54fd8f12deca4f47515f", size = 7533040, upload-time = "2024-07-19T09:26:51.238Z" }
219
+ wheels = [
220
+ { url = "https://files.pythonhosted.org/packages/b2/fe/81695a1aa331a842b582453b605175f419fe8540355886031328089d840a/sympy-1.13.1-py3-none-any.whl", hash = "sha256:db36cdc64bf61b9b24578b6f7bab1ecdd2452cf008f34faa33776680c26d66f8", size = 6189177, upload-time = "2024-07-19T09:26:48.863Z" },
221
+ ]
222
+
223
+ [[package]]
224
+ name = "torch"
225
+ version = "2.6.0+cpu"
226
+ source = { registry = "https://download.pytorch.org/whl/cpu" }
227
+ dependencies = [
228
+ { name = "filelock", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
229
+ { name = "fsspec", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
230
+ { name = "jinja2", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
231
+ { name = "networkx", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
232
+ { name = "setuptools", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
233
+ { name = "sympy", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
234
+ { name = "typing-extensions", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
235
+ ]
236
+ wheels = [
237
+ { url = "https://download-r2.pytorch.org/whl/cpu/torch-2.6.0%2Bcpu-cp312-cp312-linux_x86_64.whl", hash = "sha256:59e78aa0c690f70734e42670036d6b541930b8eabbaa18d94e090abf14cc4d91", upload-time = "2025-01-30T00:38:31Z" },
238
+ ]
239
+
240
+ [[package]]
241
+ name = "typing-extensions"
242
+ version = "4.16.0"
243
+ source = { registry = "https://pypi.org/simple" }
244
+ sdist = { url = "https://files.pythonhosted.org/packages/f6/cc/6253133b5bb138fc3306cebfbda2c520f545d36b5be2c7255cc528bb45d6/typing_extensions-4.16.0.tar.gz", hash = "sha256:dc983d19a509c94dba722ee6abd33940f7c05a89e243c47e907eb4db6f1a43e5", size = 113555, upload-time = "2026-07-02T08:40:05.92Z" }
245
+ wheels = [
246
+ { url = "https://files.pythonhosted.org/packages/49/d3/b8441a820a491ddfc024b0b0cf0393375b75ea13866d9c66727e54c2fc80/typing_extensions-4.16.0-py3-none-any.whl", hash = "sha256:481caa481374e813c1b176ada14e97f1f67a4539ce9cfeb3f350d78d6370c2e8", size = 45571, upload-time = "2026-07-02T08:40:04.659Z" },
247
+ ]
decoder.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:86953a2604f3494bf44c4d81df67c444455b4a9f0cf6f08d62fab08393bd29e1
3
+ size 1736403
encoder.onnx ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c492a8907a8b62d7f1e311c0df469490a4e4670865a6a5d8b4b7749a38666091
3
+ size 1611543