gpu-sankhya 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -21
- package/README.md +320 -307
- package/package.json +59 -59
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Atharva Kusumbia
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Atharva Kusumbia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,307 +1,320 @@
|
|
|
1
|
-
# gpu-sankhya
|
|
2
|
-
|
|
3
|
-
Parses Indian informal number/currency shorthand — Hinglish (romanised
|
|
4
|
-
Hindi) and Indian-English amount phrases like `sava lakh`, `dedh crore`,
|
|
5
|
-
`2.5L`, `20k`, `2-3 lakh` — into a clean numeric value, with the span,
|
|
6
|
-
unit, currency, and confidence that produced it.
|
|
7
|
-
|
|
8
|
-
A small char-level CNN tags each character of the input with a BIO span
|
|
9
|
-
label and a semantic token class (digit, prefix word like "sava"/"dedh",
|
|
10
|
-
cardinal number word, scale unit like lakh/crore/hazaar). A deterministic
|
|
11
|
-
arithmetic core then turns that class sequence into a number — the model
|
|
12
|
-
never predicts the value directly, so the arithmetic can't drift from the
|
|
13
|
-
grammar (prefix semantics, additive descending units, multiplicative
|
|
14
|
-
ascending units like `das hazaar crore` = 10,000 × 1 crore = 1e11, etc).
|
|
15
|
-
|
|
16
|
-
Ships with a small (27 KB gzipped) int8-quantized default model inlined
|
|
17
|
-
in the package — `import { parse } from "gpu-sankhya"` works with no
|
|
18
|
-
network fetch. Zero runtime dependencies.
|
|
19
|
-
|
|
20
|
-
Model training lives in `python/` (a separate, actively-trained
|
|
21
|
-
component); this package is the runtime that loads its exported weights.
|
|
22
|
-
See `python/README.md` if you want to train your own weights and load
|
|
23
|
-
them via `createParser({ weights })` instead of the bundled default.
|
|
24
|
-
|
|
25
|
-
## Install
|
|
26
|
-
|
|
27
|
-
```bash
|
|
28
|
-
npm install gpu-sankhya
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
## Usage
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
import { parse, parseBatch, createParser } from "gpu-sankhya";
|
|
35
|
-
|
|
36
|
-
parse("sava lakh");
|
|
37
|
-
// [{
|
|
38
|
-
// span: "sava lakh", start: 0, end: 9,
|
|
39
|
-
// value: 125000, unit: "lakh", currency: null,
|
|
40
|
-
// confidence: 0.95, classes: ["PFX_SAVA", "SEP", "UNIT_LAKH"]
|
|
41
|
-
// }]
|
|
42
|
-
|
|
43
|
-
parse("mera budget paune do lakh tak ka hai");
|
|
44
|
-
// [{ span: "paune do lakh", value: 175000, unit: "lakh", ... }]
|
|
45
|
-
|
|
46
|
-
parse("2.5L");
|
|
47
|
-
// [{ span: "2.5L", value: 250000, unit: "lakh", ... }]
|
|
48
|
-
|
|
49
|
-
parse("20k logon ne attend kiya");
|
|
50
|
-
// [{ span: "20k", value: 20000, unit: "hazaar", ... }]
|
|
51
|
-
|
|
52
|
-
parse("2-3 lakh");
|
|
53
|
-
// [{ span: "2-3 lakh", value: 200000, range: [200000, 300000], unit: "lakh", ... }]
|
|
54
|
-
|
|
55
|
-
parse("das hazaar crore");
|
|
56
|
-
// [{ span: "das hazaar crore", value: 100000000000, unit: "crore", ... }]
|
|
57
|
-
|
|
58
|
-
parse("sawaa laakh ka budget hai");
|
|
59
|
-
// [{ span: "sawaa laakh", value: 125000, unit: "lakh", ... }] -- spelling
|
|
60
|
-
// variance ("sawaa"/"sava", "laakh"/"lakh") is part of the training data,
|
|
61
|
-
// not special-cased.
|
|
62
|
-
|
|
63
|
-
// batch (uses WebGPU automatically for large batches in a browser, else CPU)
|
|
64
|
-
const results = await parseBatch(["sava lakh", "dedh crore", "..."]);
|
|
65
|
-
|
|
66
|
-
// custom / newer trained weights (see python/README.md to train your own)
|
|
67
|
-
const parser = createParser({ weights: myWeightsJson, backend: "cpu" });
|
|
68
|
-
parser.parse("paune do lakh");
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Output shape
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
interface Sankhya {
|
|
75
|
-
span: string; // exact source substring
|
|
76
|
-
start: number; end: number;
|
|
77
|
-
value: number; // resolved value; low end for ranges
|
|
78
|
-
range?: [number, number];// present only for ranges
|
|
79
|
-
unit: "sau"|"hazaar"|"lakh"|"crore"|"million"|"billion"|"arab"|"kharab"|null;
|
|
80
|
-
currency: "INR"|null; // adjacent marker detected outside the span
|
|
81
|
-
confidence: number; // mean of span-tag softmax probs over the span
|
|
82
|
-
classes: string[]; // normalised token classes, e.g. ["PFX_DHAI","UNIT_LAKH"]
|
|
83
|
-
}
|
|
84
|
-
```
|
|
85
|
-
|
|
86
|
-
### API
|
|
87
|
-
|
|
88
|
-
- **`parse(text, opts?) => Sankhya[]`** — synchronous, always CPU. Use
|
|
89
|
-
this for one-off strings; there's no async overhead.
|
|
90
|
-
- **`parseBatch(texts, opts?) => Promise<Sankhya[][]>`** — batched parse.
|
|
91
|
-
Runs on CPU by default. Pass `{ backend: "webgpu" }` to force WebGPU, or
|
|
92
|
-
`{ backend: "auto" }` to use WebGPU automatically when it's available
|
|
93
|
-
*and* the batch has at least 32 texts (otherwise CPU, since GPU
|
|
94
|
-
dispatch overhead dominates for small batches).
|
|
95
|
-
- **`createParser({ weights?, backend? })`** — build a `Parser` instance
|
|
96
|
-
around a custom weights JSON (float or int8 form, as written by
|
|
97
|
-
`python/sankhya/export.py`), instead of the bundled default model. See
|
|
98
|
-
`python/README.md` for how to train and export your own weights.
|
|
99
|
-
- **`isWebGPUAvailable()`** — true if `navigator.gpu` exists in the
|
|
100
|
-
current environment. Cheap and synchronous, but doesn't guarantee a
|
|
101
|
-
usable adapter (e.g. headless browsers without GPU access).
|
|
102
|
-
- **`probeWebGPU() => Promise<boolean>`** — authoritative async check:
|
|
103
|
-
resolves false immediately if `navigator.gpu` is missing, otherwise
|
|
104
|
-
awaits `requestAdapter()` and resolves to whether an adapter was
|
|
105
|
-
actually obtained. Result is cached, so repeated calls only probe once.
|
|
106
|
-
`parseBatch`'s `"auto"` backend uses this (not `isWebGPUAvailable()`)
|
|
107
|
-
to decide whether to try the GPU path.
|
|
108
|
-
|
|
109
|
-
## Backends
|
|
110
|
-
|
|
111
|
-
- **CPU** (default everywhere): a plain typed-array forward pass mirroring
|
|
112
|
-
the Python reference implementation exactly, tuned for throughput
|
|
113
|
-
(preallocated buffers, channels-outer loop order, no per-character
|
|
114
|
-
allocation). Runs in Node and any browser.
|
|
115
|
-
- **WebGPU**: the same forward pass as WGSL compute shaders (embedding
|
|
116
|
-
gather, one dispatch per conv1d+ReLU layer, two head matmuls), used only
|
|
117
|
-
through `parseBatch` for large batches. Device/shader setup is lazy —
|
|
118
|
-
importing the package never touches `navigator.gpu`, so it's safe to
|
|
119
|
-
import in Node or SSR. Falls back to CPU per-text for inputs longer than
|
|
120
|
-
the model's 128-char window (the sliding-window path used by `parse` is
|
|
121
|
-
CPU-only for now).
|
|
122
|
-
|
|
123
|
-
### Why CPU by default
|
|
124
|
-
|
|
125
|
-
One inference is small — roughly 0.8M multiply-adds through a 4-layer,
|
|
126
|
-
32-channel char CNN — and runs in about 1.5 ms in plain JS. A WebGPU
|
|
127
|
-
dispatch has fixed overhead of a few milliseconds (device/pipeline setup,
|
|
128
|
-
buffer upload, queue submit, readback), which dwarfs that per-string cost.
|
|
129
|
-
So the GPU only pays off once you're amortizing that overhead over a
|
|
130
|
-
batch — hundreds of strings at once — which is exactly what `parseBatch`
|
|
131
|
-
does with `backend: "auto"`/`"webgpu"`; `parse()` stays CPU-only and
|
|
132
|
-
synchronous on purpose.
|
|
133
|
-
|
|
134
|
-
The WebGPU path is implemented and type-checks, and has been exercised in
|
|
135
|
-
Node (where it falls back to CPU since there's no GPU) — it has **not**
|
|
136
|
-
yet been verified against a real GPU in a browser. Treat it as
|
|
137
|
-
implemented-but-unverified until that happens (see Roadmap).
|
|
138
|
-
|
|
139
|
-
## Accuracy
|
|
140
|
-
|
|
141
|
-
The bundled default model: 17,883 parameters, 4 conv layers (kernel sizes
|
|
142
|
-
3/5/3/3, dilations 1/1/2/4, 32 channels), 16-dim char embeddings over a
|
|
143
|
-
56-character vocab. Trained 20 epochs (~9 minutes on 4 CPU cores) on
|
|
144
|
-
150,000 synthetic examples generated from the `hi_latn` language pack's
|
|
145
|
-
grammar (see `python/README.md`).
|
|
146
|
-
|
|
147
|
-
On synthetic validation data (drawn from the same generator/templates as
|
|
148
|
-
training): 0.97 value accuracy. This number is optimistic — it's testing
|
|
149
|
-
the model on its own distribution.
|
|
150
|
-
|
|
151
|
-
On a hand-written gold set of 180 sentences / 161 spans
|
|
152
|
-
(`python/tests/gold.jsonl`), written independently of the generator:
|
|
153
|
-
|
|
154
|
-
| metric | value |
|
|
155
|
-
| --- | --- |
|
|
156
|
-
| value accuracy | 0.95 (153/161) |
|
|
157
|
-
| span precision | 0.90 |
|
|
158
|
-
| span recall | 0.96 |
|
|
159
|
-
| span F1 | 0.93 |
|
|
160
|
-
|
|
161
|
-
**The gold number is the one to trust.** Known miss categories, in rough
|
|
162
|
-
order of frequency:
|
|
163
|
-
|
|
164
|
-
- unusual typos the noise model doesn't cover (e.g. "croer" for "crore")
|
|
165
|
-
- possessive apostrophes ("do lakh's")
|
|
166
|
-
- long multi-term ranges ("paanch se sadhe saat lakh")
|
|
167
|
-
- occasional spurious spans triggered by unfamiliar words near number-ish
|
|
168
|
-
context
|
|
169
|
-
|
|
170
|
-
Reproduce these numbers yourself with
|
|
171
|
-
`python -m sankhya.eval_gold --gold tests/gold.jsonl --weights-json src/data/default-weights.json --int8`
|
|
172
|
-
from `python/` (see `python/README.md`).
|
|
173
|
-
|
|
174
|
-
## How it works
|
|
175
|
-
|
|
176
|
-
1. The input string is lowercased and char-encoded against the model's
|
|
177
|
-
vocab (unknown chars map to `<unk>`).
|
|
178
|
-
2. A 4-layer dilated conv1d stack (see Accuracy above) produces, per
|
|
179
|
-
character, a 3-way BIO logit (O/B/I) and a class logit over the
|
|
180
|
-
semantic token vocabulary (prefix words, cardinals, units, digits,
|
|
181
|
-
separators, misc).
|
|
182
|
-
3. Decoding turns those per-character predictions into spans and tokens
|
|
183
|
-
(see Decoding below).
|
|
184
|
-
4. The deterministic arithmetic core (`src/core.ts`, mirrored 1:1 from
|
|
185
|
-
`python/sankhya/core.py` and unit-tested directly in
|
|
186
|
-
`test/core.test.ts`) evaluates each span's token sequence into a
|
|
187
|
-
value: prefix semantics (sava = ×1.25, dedh = ×1.5, paune = subtract
|
|
188
|
-
1/4 from the next cardinal, ...), additive combination of descending
|
|
189
|
-
units, multiplicative combination of ascending units, and range
|
|
190
|
-
handling for `X-Y unit` / `X se Y unit` phrases.
|
|
191
|
-
|
|
192
|
-
### Decoding
|
|
193
|
-
|
|
194
|
-
Raw per-character BIO/class predictions are cleaned up before evaluation:
|
|
195
|
-
|
|
196
|
-
- **Strict BIO decode**: a span starts only at a `B` tag; an `I` that
|
|
197
|
-
isn't preceded by an open span is treated as `O`.
|
|
198
|
-
- **BIO bridging**: a single-character `O` gap inside what's otherwise a
|
|
199
|
-
contiguous span is closed (handles a stray misclassified character
|
|
200
|
-
without splitting the span in two).
|
|
201
|
-
- **Digit-run extension**: a span is extended forward through a trailing
|
|
202
|
-
run of digit characters it was cut short of.
|
|
203
|
-
- **Class repair**: within a span, per-character classes are smoothed by
|
|
204
|
-
majority vote over character-type sub-runs (fixes a stray misclassified
|
|
205
|
-
character inside an otherwise-consistent digit or letter run), plus a
|
|
206
|
-
few punctuation-specific rules.
|
|
207
|
-
- **Confidence filter**: a span's confidence is the mean of the max BIO
|
|
208
|
-
softmax probability per character; spans below 0.5 are dropped.
|
|
209
|
-
- Only after all of the above does the deterministic arithmetic core run
|
|
210
|
-
on the resulting token sequence.
|
|
211
|
-
|
|
212
|
-
One more detail that matters more than it looks like it should: the
|
|
213
|
-
runtime right-pads the character-id array with 16 pad tokens before
|
|
214
|
-
running the forward pass (mirroring `python/sankhya/np_infer.py`'s
|
|
215
|
-
`pad_ids`/`PAD_TAIL=16`), because training always right-pads every
|
|
216
|
-
example to the model's max length the same way. Running a short, tightly
|
|
217
|
-
cropped input (e.g. the bare 4 characters of `"2.5L"`) without that
|
|
218
|
-
padding measurably corrupts predictions — the model was never trained on
|
|
219
|
-
inputs that end at the literal edge of the array. The padded tail's
|
|
220
|
-
outputs are discarded; only the real characters' predictions are used.
|
|
221
|
-
|
|
222
|
-
## Limitations
|
|
223
|
-
|
|
224
|
-
- **Latin-script Hindi only.** Only Hinglish / romanised Hindi and Indian
|
|
225
|
-
English amount phrases are supported today. Devanagari script and other
|
|
226
|
-
Indian languages are planned via additional language packs (the
|
|
227
|
-
arithmetic core is already language-independent; only the class
|
|
228
|
-
vocabulary and currency-marker lists are per-language) — see Roadmap.
|
|
229
|
-
- Text longer than 128 characters is processed with a sliding window
|
|
230
|
-
(128-char windows, 16-char overlap) and results are merged/deduplicated
|
|
231
|
-
by span; extremely long inputs may still miss a span that straddles a
|
|
232
|
-
window boundary in an unlucky way.
|
|
233
|
-
- Model quality: see Accuracy above. The known miss categories there
|
|
234
|
-
(unusual typos, possessive apostrophes, long multi-term ranges,
|
|
235
|
-
occasional spurious spans) are model-quality issues, not bugs in the
|
|
236
|
-
arithmetic core, which is unit-tested directly and independently of the
|
|
237
|
-
model in `test/core.test.ts`.
|
|
238
|
-
|
|
239
|
-
## Repository layout
|
|
240
|
-
|
|
241
|
-
- `src/` — the JS/TS runtime: char encoding, CPU forward pass
|
|
242
|
-
(`infer-cpu.ts`), WebGPU forward pass, decode, the arithmetic core
|
|
243
|
-
(`core.ts`), the public API (`index.ts`), and the bundled default
|
|
244
|
-
weights (`src/data/default-weights.json`).
|
|
245
|
-
- `test/` — Node test files (`node --test`), including parity fixtures
|
|
246
|
-
generated from the Python reference implementation
|
|
247
|
-
(`test/fixtures/parity.jsonl`, `decoded.jsonl` — see
|
|
248
|
-
`python/README.md`'s "Ship to the npm package" section).
|
|
249
|
-
- `bench/` — `parse()`/`parseBatch()` latency benchmarks.
|
|
250
|
-
- `demo/` — a minimal textarea + live-results HTML demo.
|
|
251
|
-
- `python/sankhya/` — `classes.py` (shared class vocabulary), `core.py`
|
|
252
|
-
(the deterministic arithmetic core), `langs/` (language packs, e.g.
|
|
253
|
-
`hi_latn.py`), `noise_latn.py` (typo/spelling-variance injection),
|
|
254
|
-
`generator.py` (synthetic labelled-data generator), `model.py` (the
|
|
255
|
-
char CNN), `train.py`, `export.py`, `np_infer.py` (numpy reference
|
|
256
|
-
forward pass, what the JS port mirrors), `decode.py`, `eval_gold.py`,
|
|
257
|
-
`make_fixtures.py`.
|
|
258
|
-
- `python/tests/` — `test_core.py`, `test_decode.py`, `test_generator.py`,
|
|
259
|
-
and `gold.jsonl` (the hand-written gold set).
|
|
260
|
-
- `docs/DATA_GRAMMAR.md` — the data/grammar spec the generator and
|
|
261
|
-
language packs implement.
|
|
262
|
-
|
|
263
|
-
## Roadmap
|
|
264
|
-
|
|
265
|
-
Everything here is scoped to Indian languages — there's no plan to
|
|
266
|
-
support non-Indian numbering/currency shorthand.
|
|
267
|
-
|
|
268
|
-
1. **Devanagari Hindi pack** (डेढ़ लाख). A new language pack plus a new
|
|
269
|
-
noise module for Devanagari-specific variance (matra/nukta elision or
|
|
270
|
-
substitution) and a charset rebuild — no changes needed to `core.ts`/
|
|
271
|
-
`core.py` or the runtime, since the arithmetic core and BIO/class
|
|
272
|
-
architecture are already language-independent.
|
|
273
|
-
2. **Other Indian languages as packs**: Marathi (साडे, सव्वा), Gujarati
|
|
274
|
-
(સવા, દોઢ), Bengali (দেড়, আড়াই), and Tamil/Telugu/Kannada number
|
|
275
|
-
words. Same shape as (1) — a new pack, a new noise function, a charset
|
|
276
|
-
rebuild.
|
|
277
|
-
3. **WebGPU browser verification.** The WebGPU path type-checks and has
|
|
278
|
-
been exercised in Node (falling back to CPU there), but has not yet
|
|
279
|
-
been run against a real GPU in a browser — needs that verification
|
|
280
|
-
pass before it should be relied on.
|
|
281
|
-
4. **A WASM SIMD kernel**, if sub-millisecond latency is ever needed
|
|
282
|
-
beyond what the plain-JS CPU path already gives.
|
|
283
|
-
|
|
284
|
-
## Development
|
|
285
|
-
|
|
286
|
-
```bash
|
|
287
|
-
npm install
|
|
288
|
-
npm run build # esbuild -> dist/index.js (ESM), tsc -> dist/*.d.ts
|
|
289
|
-
npm test # node --test over test/*.test.ts
|
|
290
|
-
npm run bench # parse() and parseBatch() latency
|
|
291
|
-
npm run size # gzipped dist/index.js size
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
See `demo/index.html` for a minimal textarea + live-results demo that
|
|
295
|
-
imports `dist/index.js` directly (no build step needed beyond `npm run
|
|
296
|
-
build`).
|
|
297
|
-
|
|
298
|
-
## Releasing
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
1
|
+
# gpu-sankhya
|
|
2
|
+
|
|
3
|
+
Parses Indian informal number/currency shorthand — Hinglish (romanised
|
|
4
|
+
Hindi) and Indian-English amount phrases like `sava lakh`, `dedh crore`,
|
|
5
|
+
`2.5L`, `20k`, `2-3 lakh` — into a clean numeric value, with the span,
|
|
6
|
+
unit, currency, and confidence that produced it.
|
|
7
|
+
|
|
8
|
+
A small char-level CNN tags each character of the input with a BIO span
|
|
9
|
+
label and a semantic token class (digit, prefix word like "sava"/"dedh",
|
|
10
|
+
cardinal number word, scale unit like lakh/crore/hazaar). A deterministic
|
|
11
|
+
arithmetic core then turns that class sequence into a number — the model
|
|
12
|
+
never predicts the value directly, so the arithmetic can't drift from the
|
|
13
|
+
grammar (prefix semantics, additive descending units, multiplicative
|
|
14
|
+
ascending units like `das hazaar crore` = 10,000 × 1 crore = 1e11, etc).
|
|
15
|
+
|
|
16
|
+
Ships with a small (27 KB gzipped) int8-quantized default model inlined
|
|
17
|
+
in the package — `import { parse } from "gpu-sankhya"` works with no
|
|
18
|
+
network fetch. Zero runtime dependencies.
|
|
19
|
+
|
|
20
|
+
Model training lives in `python/` (a separate, actively-trained
|
|
21
|
+
component); this package is the runtime that loads its exported weights.
|
|
22
|
+
See `python/README.md` if you want to train your own weights and load
|
|
23
|
+
them via `createParser({ weights })` instead of the bundled default.
|
|
24
|
+
|
|
25
|
+
## Install
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm install gpu-sankhya
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { parse, parseBatch, createParser } from "gpu-sankhya";
|
|
35
|
+
|
|
36
|
+
parse("sava lakh");
|
|
37
|
+
// [{
|
|
38
|
+
// span: "sava lakh", start: 0, end: 9,
|
|
39
|
+
// value: 125000, unit: "lakh", currency: null,
|
|
40
|
+
// confidence: 0.95, classes: ["PFX_SAVA", "SEP", "UNIT_LAKH"]
|
|
41
|
+
// }]
|
|
42
|
+
|
|
43
|
+
parse("mera budget paune do lakh tak ka hai");
|
|
44
|
+
// [{ span: "paune do lakh", value: 175000, unit: "lakh", ... }]
|
|
45
|
+
|
|
46
|
+
parse("2.5L");
|
|
47
|
+
// [{ span: "2.5L", value: 250000, unit: "lakh", ... }]
|
|
48
|
+
|
|
49
|
+
parse("20k logon ne attend kiya");
|
|
50
|
+
// [{ span: "20k", value: 20000, unit: "hazaar", ... }]
|
|
51
|
+
|
|
52
|
+
parse("2-3 lakh");
|
|
53
|
+
// [{ span: "2-3 lakh", value: 200000, range: [200000, 300000], unit: "lakh", ... }]
|
|
54
|
+
|
|
55
|
+
parse("das hazaar crore");
|
|
56
|
+
// [{ span: "das hazaar crore", value: 100000000000, unit: "crore", ... }]
|
|
57
|
+
|
|
58
|
+
parse("sawaa laakh ka budget hai");
|
|
59
|
+
// [{ span: "sawaa laakh", value: 125000, unit: "lakh", ... }] -- spelling
|
|
60
|
+
// variance ("sawaa"/"sava", "laakh"/"lakh") is part of the training data,
|
|
61
|
+
// not special-cased.
|
|
62
|
+
|
|
63
|
+
// batch (uses WebGPU automatically for large batches in a browser, else CPU)
|
|
64
|
+
const results = await parseBatch(["sava lakh", "dedh crore", "..."]);
|
|
65
|
+
|
|
66
|
+
// custom / newer trained weights (see python/README.md to train your own)
|
|
67
|
+
const parser = createParser({ weights: myWeightsJson, backend: "cpu" });
|
|
68
|
+
parser.parse("paune do lakh");
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Output shape
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
interface Sankhya {
|
|
75
|
+
span: string; // exact source substring
|
|
76
|
+
start: number; end: number;
|
|
77
|
+
value: number; // resolved value; low end for ranges
|
|
78
|
+
range?: [number, number];// present only for ranges
|
|
79
|
+
unit: "sau"|"hazaar"|"lakh"|"crore"|"million"|"billion"|"arab"|"kharab"|null;
|
|
80
|
+
currency: "INR"|null; // adjacent marker detected outside the span
|
|
81
|
+
confidence: number; // mean of span-tag softmax probs over the span
|
|
82
|
+
classes: string[]; // normalised token classes, e.g. ["PFX_DHAI","UNIT_LAKH"]
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### API
|
|
87
|
+
|
|
88
|
+
- **`parse(text, opts?) => Sankhya[]`** — synchronous, always CPU. Use
|
|
89
|
+
this for one-off strings; there's no async overhead.
|
|
90
|
+
- **`parseBatch(texts, opts?) => Promise<Sankhya[][]>`** — batched parse.
|
|
91
|
+
Runs on CPU by default. Pass `{ backend: "webgpu" }` to force WebGPU, or
|
|
92
|
+
`{ backend: "auto" }` to use WebGPU automatically when it's available
|
|
93
|
+
*and* the batch has at least 32 texts (otherwise CPU, since GPU
|
|
94
|
+
dispatch overhead dominates for small batches).
|
|
95
|
+
- **`createParser({ weights?, backend? })`** — build a `Parser` instance
|
|
96
|
+
around a custom weights JSON (float or int8 form, as written by
|
|
97
|
+
`python/sankhya/export.py`), instead of the bundled default model. See
|
|
98
|
+
`python/README.md` for how to train and export your own weights.
|
|
99
|
+
- **`isWebGPUAvailable()`** — true if `navigator.gpu` exists in the
|
|
100
|
+
current environment. Cheap and synchronous, but doesn't guarantee a
|
|
101
|
+
usable adapter (e.g. headless browsers without GPU access).
|
|
102
|
+
- **`probeWebGPU() => Promise<boolean>`** — authoritative async check:
|
|
103
|
+
resolves false immediately if `navigator.gpu` is missing, otherwise
|
|
104
|
+
awaits `requestAdapter()` and resolves to whether an adapter was
|
|
105
|
+
actually obtained. Result is cached, so repeated calls only probe once.
|
|
106
|
+
`parseBatch`'s `"auto"` backend uses this (not `isWebGPUAvailable()`)
|
|
107
|
+
to decide whether to try the GPU path.
|
|
108
|
+
|
|
109
|
+
## Backends
|
|
110
|
+
|
|
111
|
+
- **CPU** (default everywhere): a plain typed-array forward pass mirroring
|
|
112
|
+
the Python reference implementation exactly, tuned for throughput
|
|
113
|
+
(preallocated buffers, channels-outer loop order, no per-character
|
|
114
|
+
allocation). Runs in Node and any browser.
|
|
115
|
+
- **WebGPU**: the same forward pass as WGSL compute shaders (embedding
|
|
116
|
+
gather, one dispatch per conv1d+ReLU layer, two head matmuls), used only
|
|
117
|
+
through `parseBatch` for large batches. Device/shader setup is lazy —
|
|
118
|
+
importing the package never touches `navigator.gpu`, so it's safe to
|
|
119
|
+
import in Node or SSR. Falls back to CPU per-text for inputs longer than
|
|
120
|
+
the model's 128-char window (the sliding-window path used by `parse` is
|
|
121
|
+
CPU-only for now).
|
|
122
|
+
|
|
123
|
+
### Why CPU by default
|
|
124
|
+
|
|
125
|
+
One inference is small — roughly 0.8M multiply-adds through a 4-layer,
|
|
126
|
+
32-channel char CNN — and runs in about 1.5 ms in plain JS. A WebGPU
|
|
127
|
+
dispatch has fixed overhead of a few milliseconds (device/pipeline setup,
|
|
128
|
+
buffer upload, queue submit, readback), which dwarfs that per-string cost.
|
|
129
|
+
So the GPU only pays off once you're amortizing that overhead over a
|
|
130
|
+
batch — hundreds of strings at once — which is exactly what `parseBatch`
|
|
131
|
+
does with `backend: "auto"`/`"webgpu"`; `parse()` stays CPU-only and
|
|
132
|
+
synchronous on purpose.
|
|
133
|
+
|
|
134
|
+
The WebGPU path is implemented and type-checks, and has been exercised in
|
|
135
|
+
Node (where it falls back to CPU since there's no GPU) — it has **not**
|
|
136
|
+
yet been verified against a real GPU in a browser. Treat it as
|
|
137
|
+
implemented-but-unverified until that happens (see Roadmap).
|
|
138
|
+
|
|
139
|
+
## Accuracy
|
|
140
|
+
|
|
141
|
+
The bundled default model: 17,883 parameters, 4 conv layers (kernel sizes
|
|
142
|
+
3/5/3/3, dilations 1/1/2/4, 32 channels), 16-dim char embeddings over a
|
|
143
|
+
56-character vocab. Trained 20 epochs (~9 minutes on 4 CPU cores) on
|
|
144
|
+
150,000 synthetic examples generated from the `hi_latn` language pack's
|
|
145
|
+
grammar (see `python/README.md`).
|
|
146
|
+
|
|
147
|
+
On synthetic validation data (drawn from the same generator/templates as
|
|
148
|
+
training): 0.97 value accuracy. This number is optimistic — it's testing
|
|
149
|
+
the model on its own distribution.
|
|
150
|
+
|
|
151
|
+
On a hand-written gold set of 180 sentences / 161 spans
|
|
152
|
+
(`python/tests/gold.jsonl`), written independently of the generator:
|
|
153
|
+
|
|
154
|
+
| metric | value |
|
|
155
|
+
| --- | --- |
|
|
156
|
+
| value accuracy | 0.95 (153/161) |
|
|
157
|
+
| span precision | 0.90 |
|
|
158
|
+
| span recall | 0.96 |
|
|
159
|
+
| span F1 | 0.93 |
|
|
160
|
+
|
|
161
|
+
**The gold number is the one to trust.** Known miss categories, in rough
|
|
162
|
+
order of frequency:
|
|
163
|
+
|
|
164
|
+
- unusual typos the noise model doesn't cover (e.g. "croer" for "crore")
|
|
165
|
+
- possessive apostrophes ("do lakh's")
|
|
166
|
+
- long multi-term ranges ("paanch se sadhe saat lakh")
|
|
167
|
+
- occasional spurious spans triggered by unfamiliar words near number-ish
|
|
168
|
+
context
|
|
169
|
+
|
|
170
|
+
Reproduce these numbers yourself with
|
|
171
|
+
`python -m sankhya.eval_gold --gold tests/gold.jsonl --weights-json src/data/default-weights.json --int8`
|
|
172
|
+
from `python/` (see `python/README.md`).
|
|
173
|
+
|
|
174
|
+
## How it works
|
|
175
|
+
|
|
176
|
+
1. The input string is lowercased and char-encoded against the model's
|
|
177
|
+
vocab (unknown chars map to `<unk>`).
|
|
178
|
+
2. A 4-layer dilated conv1d stack (see Accuracy above) produces, per
|
|
179
|
+
character, a 3-way BIO logit (O/B/I) and a class logit over the
|
|
180
|
+
semantic token vocabulary (prefix words, cardinals, units, digits,
|
|
181
|
+
separators, misc).
|
|
182
|
+
3. Decoding turns those per-character predictions into spans and tokens
|
|
183
|
+
(see Decoding below).
|
|
184
|
+
4. The deterministic arithmetic core (`src/core.ts`, mirrored 1:1 from
|
|
185
|
+
`python/sankhya/core.py` and unit-tested directly in
|
|
186
|
+
`test/core.test.ts`) evaluates each span's token sequence into a
|
|
187
|
+
value: prefix semantics (sava = ×1.25, dedh = ×1.5, paune = subtract
|
|
188
|
+
1/4 from the next cardinal, ...), additive combination of descending
|
|
189
|
+
units, multiplicative combination of ascending units, and range
|
|
190
|
+
handling for `X-Y unit` / `X se Y unit` phrases.
|
|
191
|
+
|
|
192
|
+
### Decoding
|
|
193
|
+
|
|
194
|
+
Raw per-character BIO/class predictions are cleaned up before evaluation:
|
|
195
|
+
|
|
196
|
+
- **Strict BIO decode**: a span starts only at a `B` tag; an `I` that
|
|
197
|
+
isn't preceded by an open span is treated as `O`.
|
|
198
|
+
- **BIO bridging**: a single-character `O` gap inside what's otherwise a
|
|
199
|
+
contiguous span is closed (handles a stray misclassified character
|
|
200
|
+
without splitting the span in two).
|
|
201
|
+
- **Digit-run extension**: a span is extended forward through a trailing
|
|
202
|
+
run of digit characters it was cut short of.
|
|
203
|
+
- **Class repair**: within a span, per-character classes are smoothed by
|
|
204
|
+
majority vote over character-type sub-runs (fixes a stray misclassified
|
|
205
|
+
character inside an otherwise-consistent digit or letter run), plus a
|
|
206
|
+
few punctuation-specific rules.
|
|
207
|
+
- **Confidence filter**: a span's confidence is the mean of the max BIO
|
|
208
|
+
softmax probability per character; spans below 0.5 are dropped.
|
|
209
|
+
- Only after all of the above does the deterministic arithmetic core run
|
|
210
|
+
on the resulting token sequence.
|
|
211
|
+
|
|
212
|
+
One more detail that matters more than it looks like it should: the
|
|
213
|
+
runtime right-pads the character-id array with 16 pad tokens before
|
|
214
|
+
running the forward pass (mirroring `python/sankhya/np_infer.py`'s
|
|
215
|
+
`pad_ids`/`PAD_TAIL=16`), because training always right-pads every
|
|
216
|
+
example to the model's max length the same way. Running a short, tightly
|
|
217
|
+
cropped input (e.g. the bare 4 characters of `"2.5L"`) without that
|
|
218
|
+
padding measurably corrupts predictions — the model was never trained on
|
|
219
|
+
inputs that end at the literal edge of the array. The padded tail's
|
|
220
|
+
outputs are discarded; only the real characters' predictions are used.
|
|
221
|
+
|
|
222
|
+
## Limitations
|
|
223
|
+
|
|
224
|
+
- **Latin-script Hindi only.** Only Hinglish / romanised Hindi and Indian
|
|
225
|
+
English amount phrases are supported today. Devanagari script and other
|
|
226
|
+
Indian languages are planned via additional language packs (the
|
|
227
|
+
arithmetic core is already language-independent; only the class
|
|
228
|
+
vocabulary and currency-marker lists are per-language) — see Roadmap.
|
|
229
|
+
- Text longer than 128 characters is processed with a sliding window
|
|
230
|
+
(128-char windows, 16-char overlap) and results are merged/deduplicated
|
|
231
|
+
by span; extremely long inputs may still miss a span that straddles a
|
|
232
|
+
window boundary in an unlucky way.
|
|
233
|
+
- Model quality: see Accuracy above. The known miss categories there
|
|
234
|
+
(unusual typos, possessive apostrophes, long multi-term ranges,
|
|
235
|
+
occasional spurious spans) are model-quality issues, not bugs in the
|
|
236
|
+
arithmetic core, which is unit-tested directly and independently of the
|
|
237
|
+
model in `test/core.test.ts`.
|
|
238
|
+
|
|
239
|
+
## Repository layout
|
|
240
|
+
|
|
241
|
+
- `src/` — the JS/TS runtime: char encoding, CPU forward pass
|
|
242
|
+
(`infer-cpu.ts`), WebGPU forward pass, decode, the arithmetic core
|
|
243
|
+
(`core.ts`), the public API (`index.ts`), and the bundled default
|
|
244
|
+
weights (`src/data/default-weights.json`).
|
|
245
|
+
- `test/` — Node test files (`node --test`), including parity fixtures
|
|
246
|
+
generated from the Python reference implementation
|
|
247
|
+
(`test/fixtures/parity.jsonl`, `decoded.jsonl` — see
|
|
248
|
+
`python/README.md`'s "Ship to the npm package" section).
|
|
249
|
+
- `bench/` — `parse()`/`parseBatch()` latency benchmarks.
|
|
250
|
+
- `demo/` — a minimal textarea + live-results HTML demo.
|
|
251
|
+
- `python/sankhya/` — `classes.py` (shared class vocabulary), `core.py`
|
|
252
|
+
(the deterministic arithmetic core), `langs/` (language packs, e.g.
|
|
253
|
+
`hi_latn.py`), `noise_latn.py` (typo/spelling-variance injection),
|
|
254
|
+
`generator.py` (synthetic labelled-data generator), `model.py` (the
|
|
255
|
+
char CNN), `train.py`, `export.py`, `np_infer.py` (numpy reference
|
|
256
|
+
forward pass, what the JS port mirrors), `decode.py`, `eval_gold.py`,
|
|
257
|
+
`make_fixtures.py`.
|
|
258
|
+
- `python/tests/` — `test_core.py`, `test_decode.py`, `test_generator.py`,
|
|
259
|
+
and `gold.jsonl` (the hand-written gold set).
|
|
260
|
+
- `docs/DATA_GRAMMAR.md` — the data/grammar spec the generator and
|
|
261
|
+
language packs implement.
|
|
262
|
+
|
|
263
|
+
## Roadmap
|
|
264
|
+
|
|
265
|
+
Everything here is scoped to Indian languages — there's no plan to
|
|
266
|
+
support non-Indian numbering/currency shorthand.
|
|
267
|
+
|
|
268
|
+
1. **Devanagari Hindi pack** (डेढ़ लाख). A new language pack plus a new
|
|
269
|
+
noise module for Devanagari-specific variance (matra/nukta elision or
|
|
270
|
+
substitution) and a charset rebuild — no changes needed to `core.ts`/
|
|
271
|
+
`core.py` or the runtime, since the arithmetic core and BIO/class
|
|
272
|
+
architecture are already language-independent.
|
|
273
|
+
2. **Other Indian languages as packs**: Marathi (साडे, सव्वा), Gujarati
|
|
274
|
+
(સવા, દોઢ), Bengali (দেড়, আড়াই), and Tamil/Telugu/Kannada number
|
|
275
|
+
words. Same shape as (1) — a new pack, a new noise function, a charset
|
|
276
|
+
rebuild.
|
|
277
|
+
3. **WebGPU browser verification.** The WebGPU path type-checks and has
|
|
278
|
+
been exercised in Node (falling back to CPU there), but has not yet
|
|
279
|
+
been run against a real GPU in a browser — needs that verification
|
|
280
|
+
pass before it should be relied on.
|
|
281
|
+
4. **A WASM SIMD kernel**, if sub-millisecond latency is ever needed
|
|
282
|
+
beyond what the plain-JS CPU path already gives.
|
|
283
|
+
|
|
284
|
+
## Development
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
npm install
|
|
288
|
+
npm run build # esbuild -> dist/index.js (ESM), tsc -> dist/*.d.ts
|
|
289
|
+
npm test # node --test over test/*.test.ts
|
|
290
|
+
npm run bench # parse() and parseBatch() latency
|
|
291
|
+
npm run size # gzipped dist/index.js size
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
See `demo/index.html` for a minimal textarea + live-results demo that
|
|
295
|
+
imports `dist/index.js` directly (no build step needed beyond `npm run
|
|
296
|
+
build`).
|
|
297
|
+
|
|
298
|
+
## Releasing
|
|
299
|
+
|
|
300
|
+
The normal flow: bump the version and push to master.
|
|
301
|
+
|
|
302
|
+
```bash
|
|
303
|
+
npm version patch|minor|major # bumps package.json and commits + tags locally
|
|
304
|
+
git push --follow-tags origin master
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
You can also just edit the `version` field in `package.json` in a PR — no
|
|
308
|
+
need to run `npm version` or create a tag yourself. Either way, once the
|
|
309
|
+
new version lands on master, the `publish` workflow detects that
|
|
310
|
+
`package.json`'s version isn't on npm yet, builds, tests, publishes (with
|
|
311
|
+
provenance), and creates the matching git tag and GitHub release for you.
|
|
312
|
+
|
|
313
|
+
Publishing a GitHub release directly, or running the workflow manually via
|
|
314
|
+
`workflow_dispatch`, also triggers a publish.
|
|
315
|
+
|
|
316
|
+
Trusted publishing (OIDC, no `NPM_TOKEN`) must be configured once on
|
|
317
|
+
npmjs.com for this to work: package page -> Settings -> Trusted publisher,
|
|
318
|
+
with Organization/user `athrvk`, Repository `gpu-sankhya`, Workflow
|
|
319
|
+
filename `publish.yml`. The first release was published manually with
|
|
320
|
+
`npm publish`.
|
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "gpu-sankhya",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "Fast char-CNN extraction of Hinglish (Latin-script Hindi) numeric quantities, with CPU and WebGPU backends.",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/athrvk/gpu-sankhya.git"
|
|
10
|
-
},
|
|
11
|
-
"homepage": "https://athrvk.github.io/gpu-sankhya/",
|
|
12
|
-
"bugs": {
|
|
13
|
-
"url": "https://github.com/athrvk/gpu-sankhya/issues"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [
|
|
16
|
-
"hindi",
|
|
17
|
-
"hinglish",
|
|
18
|
-
"lakh",
|
|
19
|
-
"crore",
|
|
20
|
-
"number-parsing",
|
|
21
|
-
"nlp",
|
|
22
|
-
"webgpu",
|
|
23
|
-
"indian-numbering",
|
|
24
|
-
"char-cnn",
|
|
25
|
-
"onnx"
|
|
26
|
-
],
|
|
27
|
-
"author": "Atharva Kusumbia <atharvakusumbia@gmail.com>",
|
|
28
|
-
"engines": {
|
|
29
|
-
"node": ">=20"
|
|
30
|
-
},
|
|
31
|
-
"publishConfig": {
|
|
32
|
-
"access": "public"
|
|
33
|
-
},
|
|
34
|
-
"exports": {
|
|
35
|
-
".": {
|
|
36
|
-
"types": "./dist/index.d.ts",
|
|
37
|
-
"import": "./dist/index.js"
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
"types": "./dist/index.d.ts",
|
|
41
|
-
"files": [
|
|
42
|
-
"dist"
|
|
43
|
-
],
|
|
44
|
-
"sideEffects": false,
|
|
45
|
-
"scripts": {
|
|
46
|
-
"build": "node scripts/build.mjs && tsc -p tsconfig.json --emitDeclarationOnly && node scripts/fix-dts-ext.mjs",
|
|
47
|
-
"test": "node --test --experimental-strip-types test/*.test.ts",
|
|
48
|
-
"bench": "node --experimental-strip-types bench/bench.mjs",
|
|
49
|
-
"size": "node scripts/size.mjs",
|
|
50
|
-
"site": "node scripts/build.mjs && node scripts/site.mjs",
|
|
51
|
-
"prepublishOnly": "npm run build && npm test"
|
|
52
|
-
},
|
|
53
|
-
"devDependencies": {
|
|
54
|
-
"@webgpu/types": "^0.1.72",
|
|
55
|
-
"esbuild": "^0.28.2",
|
|
56
|
-
"playwright-core": "^1.63.0",
|
|
57
|
-
"typescript": "^5.9.3"
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "gpu-sankhya",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Fast char-CNN extraction of Hinglish (Latin-script Hindi) numeric quantities, with CPU and WebGPU backends.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/athrvk/gpu-sankhya.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://athrvk.github.io/gpu-sankhya/",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/athrvk/gpu-sankhya/issues"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"hindi",
|
|
17
|
+
"hinglish",
|
|
18
|
+
"lakh",
|
|
19
|
+
"crore",
|
|
20
|
+
"number-parsing",
|
|
21
|
+
"nlp",
|
|
22
|
+
"webgpu",
|
|
23
|
+
"indian-numbering",
|
|
24
|
+
"char-cnn",
|
|
25
|
+
"onnx"
|
|
26
|
+
],
|
|
27
|
+
"author": "Atharva Kusumbia <atharvakusumbia@gmail.com>",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=20"
|
|
30
|
+
},
|
|
31
|
+
"publishConfig": {
|
|
32
|
+
"access": "public"
|
|
33
|
+
},
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.js"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"files": [
|
|
42
|
+
"dist"
|
|
43
|
+
],
|
|
44
|
+
"sideEffects": false,
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "node scripts/build.mjs && tsc -p tsconfig.json --emitDeclarationOnly && node scripts/fix-dts-ext.mjs",
|
|
47
|
+
"test": "node --test --experimental-strip-types test/*.test.ts",
|
|
48
|
+
"bench": "node --experimental-strip-types bench/bench.mjs",
|
|
49
|
+
"size": "node scripts/size.mjs",
|
|
50
|
+
"site": "node scripts/build.mjs && node scripts/site.mjs",
|
|
51
|
+
"prepublishOnly": "npm run build && npm test"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@webgpu/types": "^0.1.72",
|
|
55
|
+
"esbuild": "^0.28.2",
|
|
56
|
+
"playwright-core": "^1.63.0",
|
|
57
|
+
"typescript": "^5.9.3"
|
|
58
|
+
}
|
|
59
|
+
}
|