Skip to content

Commit 23afb22

Browse files
author
Ronald Tse
committed
docs: ML model distribution plan (TODO.distribution/)
15-file plan covering how Interscript ML models reach every audience: - 00 Overview: hybrid MECE channel architecture (GH Releases primary, HuggingFace canonical, jsdelivr edge) with audience-to-channel map - 01 GitHub Releases: per-task tag conventions, asset naming, auto-generated release notes - 02 HuggingFace Hub: org setup, per-task model repos, datasets, auto-ONNX conversion - 03 CDN strategy: jsdelivr URL convention, triple-fallback chain, Service Worker caching - 04 npm packages: @interscript/models manifest + @interscript/model-* offline bundles - 05 Ruby model cache: ~/.cache/interscript, SHA256-verify, atomic rename, LRU pruning - 06 Versioning: per-task SemVer, compatibility matrix, pre-release flow, deprecation - 07 CI/CD: release.yml workflow (build → benchmark → release → HF → npm) with idempotency guarantees - 08 Supply chain: SHA256 sidecars + SLSA provenance + Sigstore signing (three layers, MECE) - 09 Model cards: template with auto-substitution, HF rendering, i18n variants - 10 Benchmarks: DER/PER + latency p50/p95/p99, regression alerts, live leaderboard, reproducibility scripts - 11 Offline install: 3 patterns (prefetch+tarball, private mirror, local CDN) for air-gapped networks - 12 Mobile + edge: fp32/fp16/q8/q4/tflite/coreml format matrix, mobile SDK packages - 13 Telemetry: opt-in, anonymous, privacy-first (no content, no IPs) - 14 Sunset / kill switch: manifest retraction, multi-channel yank, incident response procedure README updated with distribution summary.
1 parent ab09dfd commit 23afb22

16 files changed

Lines changed: 2078 additions & 0 deletions

‎README.md‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,25 @@ Framework tests run without torch (CPU-only, fast). Training and ONNX
9696
export tests are gated behind `@pytest.mark.gpu` and require the
9797
`[train]` and `[export]` extras.
9898

99+
## Distribution
100+
101+
Models reach end users through three channels (full plan in
102+
[`TODO.distribution/`](./TODO.distribution/)):
103+
104+
| Channel | Audience | Why |
105+
|---|---|---|
106+
| **GitHub Releases** (primary) | All consumers | Versioned, immutable, checksums, tied to source tags |
107+
| **HuggingFace Hub** (canonical) | Researchers | Model cards, datasets, auto-conversion, inference API |
108+
| **jsdelivr CDN** (edge) | Browser | Edge-cached, CORS-friendly, no rate limits |
109+
110+
Per-task versioning: `rababa_arabic-v1.0.0`, `secryst_thai_ipa-v1.2.0`,
111+
etc. Each release ships fp32 + int8 + int4 variants with SHA256
112+
sidecars, SLSA provenance, and Sigstore signatures.
113+
114+
Distribution phases (P2–P8) are tracked in `TODO.distribution/`. The
115+
first production release lands when phase P6 (first trained model)
116+
completes.
117+
99118
## License
100119

101120
MIT. Model weights are released under their own licenses (see

‎TODO.distribution/00-overview.md‎

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# ML Model Distribution — Unified Plan
2+
3+
**Goal:** Every Interscript ML model is trivially installable, auto-updating,
4+
reproducible, and verifiable. One source-of-truth, three delivery channels,
5+
zero manual steps after a `git tag`.
6+
7+
## Audiences (real, identified)
8+
9+
| ID | Audience | Job to be done | Touchpoint |
10+
|---|---|---|---|
11+
| A1 | Web end-user | Type Arabic, get harakat | interscript.org, SW-cached ONNX |
12+
| A2 | JS dev | `npm i`, call API, done | `interscript-ts` auto-fetches from CDN |
13+
| A3 | Ruby dev | `gem install`, call API | `~/.cache/interscript/` lazy download |
14+
| A4 | Researcher | Read card, fine-tune, reproduce DER | HuggingFace Hub + this repo |
15+
| A5 | Mobile dev | Tiny ONNX, bundle in app | GH Releases (quantized variant) |
16+
| A6 | Other-lang author | Implement IR + fetch model | JSON IR spec + stable release URLs |
17+
18+
## Channel architecture (hybrid, MECE)
19+
20+
```
21+
Source code → github.com/interscript/ml-models
22+
(this repo: framework, configs, tests, CI/CD,
23+
release changelog, issue tracking)
24+
25+
Trained weights → huggingface.co/interscript/<task_name>
26+
(PyTorch, full) (model card, datasets, transformers.js hook,
27+
free Cloudflare-backed CDN, researcher entry)
28+
29+
Browser-ready ONNX → github.com/interscript/ml-models/releases/tag/<task>-v<X.Y.Z>
30+
(one .onnx asset per release; checksums attached;
31+
stable immutable URLs; programmatic via `gh`)
32+
33+
CDN mirror → jsdelivr.net/gh/interscript/ml-models@<tag>/...
34+
(global edge cache of the GH release assets;
35+
CORS-friendly; no rate limits for end users)
36+
37+
JS glue package → npm: @interscript/models
38+
(tiny manifest of current versions per task;
39+
consumed by interscript-ts to resolve URLs)
40+
41+
Ruby model cache → ~/.cache/interscript/<task>/<version>/model.onnx
42+
(downloaded lazily from GH releases on first call;
43+
SHA256-verified; atomic rename on success)
44+
```
45+
46+
**Why this split (not "pick one"):**
47+
48+
- HuggingFace is the only channel researchers trust. Without it we're invisible to A4.
49+
- GH Releases is the only channel with version-pinned immutable URLs tied to source tags. Without it we can't guarantee reproducibility for A5/A6.
50+
- jsdelivr is the only channel with edge cache + CORS for browsers. Without it A1 pays full latency.
51+
- npm manifest lets A2 self-host or air-gap by pinning the manifest.
52+
- Ruby cache lets A3 work without bundling 6MB in a gem.
53+
54+
Each channel serves a real audience. None is redundant.
55+
56+
## File index
57+
58+
- `00-overview.md` — this file
59+
- `01-github-releases.md` — release asset layout, tag conventions, asset naming
60+
- `02-huggingface-hub.md` — HF org, model cards, datasets, auto-conversion
61+
- `03-cdn-strategy.md` — jsdelivr + GH releases URL convention, fallback chain
62+
- `04-npm-packages.md` — `@interscript/models` manifest package
63+
- `05-ruby-model-cache.md` — gem-side download + verify + atomic write
64+
- `06-versioning-scheme.md` — per-task semver, breaking-change rules
65+
- `07-ci-cd.md` — `.github/workflows/release.yml` full design
66+
- `08-supply-chain.md` — SHA256 checksums, SLSA provenance, Sigstore signing
67+
- `09-model-cards.md` — per-task card template + automation
68+
- `10-benchmarks.md` — DER/PER publication, regression alerts
69+
- `11-offline-install.md` — air-gapped / private registry / mirror
70+
- `12-mobile-edge.md` — quantized variants, format matrix
71+
- `13-telemetry.md` — opt-in usage signals, privacy posture
72+
- `14-sunset-kill-switch.md` — what happens when a model is bad
73+
74+
## Phase schedule (each phase delivers standalone value)
75+
76+
| Phase | Outcome | Audience unlocked |
77+
|---|---|---|
78+
| **P1: Foundation** (done) | Framework + tests committed | A4 (read-only) |
79+
| **P2: Release infra** | Tag → ONNX → GH release auto | A5/A6 (binary) |
80+
| **P3: HF integration** | `hf.co/interscript/<task>` live | A4 (full) |
81+
| **P4: CDN + JS** | interscript-ts auto-loads from CDN | A1/A2 |
82+
| **P5: Ruby cache** | `gem install` works out-of-box | A3 |
83+
| **P6: First trained model** | rababa_arabic-v1.0.0 released | All |
84+
| **P7: Quantized variants** | Mobile-ready assets | A5 (mobile) |
85+
| **P8: Benchmark bot** | DER regression alerts on PR | A4 (internal) |
86+
87+
## Why GH Releases is the right primary channel (for our model sizes)
88+
89+
**The case for GH Releases as primary:**
90+
91+
1. **Size fit.** Our student ONNX is ~6MB. GH Releases supports up to 2GB
92+
per asset. HuggingFace LFS also fine. But GH Releases is the only one
93+
that gives us **release notes UI** tied to a source tag — that's the
94+
unit of "we shipped a thing".
95+
96+
2. **Reproducibility.** A `git tag rababa_arabic-v1.0.0` is a permanent
97+
pointer to a specific source state. The release attached to it is a
98+
permanent pointer to a specific binary. Researchers and CI pipelines
99+
can pin to that tag forever. HF Hub versions are similar but the
100+
release-notes UX is missing.
101+
102+
3. **Decoupling.** If HuggingFace changes ToS, gets acquired, or rate-
103+
limits, our CDN consumers (interscript-ts, interscript-ruby) keep
104+
working because they hit GH Releases / jsdelivr.
105+
106+
4. **Checksums first-class.** GH Releases API exposes asset digests;
107+
we add SHA256 sidecar files for explicit verification.
108+
109+
5. **Discovery via npm.** The npm manifest package points to GH release
110+
URLs. JS consumers never see HF. Researchers never see npm. Each
111+
audience gets the channel they expect.
112+
113+
**HuggingFace stays canonical for:**
114+
- Model cards (structured metadata, search, demo API)
115+
- Datasets (Tashkeela++ processed version)
116+
- Auto-conversion to ONNX/TF-Flax/GGUF via HF Hub
117+
- Programmatic inference API (free tier)
118+
119+
**Hierarchy:** HF = authoring surface. GH Releases = delivery surface.
120+
jsdelivr = edge cache. npm = discovery.
121+
122+
## Design principles applied
123+
124+
1. **OCP.** Adding a new task = new directory under `src/tasks/` + new
125+
release tag pattern + new HF model repo. Zero edits to release infra.
126+
2. **MECE.** HF owns the researcher surface. GH owns the binary surface.
127+
npm owns the JS discovery surface. No overlap.
128+
3. **DRY.** The release workflow runs the same export code locally and
129+
in CI. The CDN URL convention is computed from the tag, not stored.
130+
4. **Performance.** jsdelivr edge cache means a request from Tokyo hits
131+
a Tokyo POP, not GitHub's servers. First-load latency < 200ms.
132+
5. **Reversibility.** Tags are immutable but new releases can supersede.
133+
Bad releases get a `retracted` flag in the manifest; consumers
134+
auto-upgrade.
135+
6. **No AI attribution in release notes.** Per project conventions.
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# 01 — GitHub Releases architecture
2+
3+
**Status:** SPECIFICATION
4+
**Priority:** P2 (after foundation)
5+
6+
## Goal
7+
8+
`git tag rababa_arabic-v1.0.0 && git push origin rababa_arabic-v1.0.0`
9+
produces a public release with the ONNX asset, checksum, model card
10+
excerpt, and DER benchmark attached. Zero manual steps after the tag.
11+
12+
## Tag conventions
13+
14+
Tags are **per-task** to allow independent versioning:
15+
16+
```
17+
rababa_arabic-v1.0.0
18+
rababa_arabic-v1.0.1
19+
rababa_arabic-v1.1.0
20+
rababa_hebrew-v1.0.0
21+
secryst_thai_ipa-v1.0.0
22+
```
23+
24+
Why per-task tags (not repo-level `v1.0.0`):
25+
- A rababa regression shouldn't force a secryst re-release.
26+
- Each task has its own DER/PER history.
27+
- npm manifest can list independent versions per task.
28+
29+
## Release asset layout
30+
31+
Each release carries:
32+
33+
```
34+
rababa_arabic-v1.0.0/
35+
├── rababa_arabic.onnx # fp32 student model (~6MB)
36+
├── rababa_arabic-q8.onnx # int8 quantized (~1.5MB, mobile)
37+
├── rababa_arabic.onnx.sha256 # checksum sidecar
38+
├── rababa_arabic-vocab.json # input/output vocabularies
39+
├── rababa_arabic-config.json # inference config (max_seq_len, etc.)
40+
├── rababa_arabic-benchmarks.json # DER, PER, latency p50/p95/p99
41+
├── rababa_arabic-provenance.json # SLSA build provenance
42+
└── README.md # auto-generated from model card
43+
```
44+
45+
## Naming convention (MECE)
46+
47+
Asset filenames follow `<task>-<variant>.<ext>`:
48+
- `<task>.onnx` — default fp32
49+
- `<task>-q8.onnx` — int8 quantized
50+
- `<task>-q4.onnx` — int4 quantized (smallest)
51+
- `<task>-fp16.onnx` — half-precision (GPU)
52+
- `<task>-vocab.json` — vocab metadata
53+
- `<task>-config.json` — inference config
54+
- `<task>-benchmarks.json` — eval results
55+
- `<task>-provenance.json` — supply chain
56+
57+
The CDN URL is computable from `<task>` + `<version>` + `<variant>`:
58+
59+
```
60+
https://github.com/interscript/ml-models/releases/download/<task>-v<ver>/<task>-<variant>.onnx
61+
```
62+
63+
No central manifest needed to compute URLs. The convention IS the API.
64+
65+
## Release notes template
66+
67+
Auto-generated by the release workflow from:
68+
- Git log since last tag (filtered to task-relevant commits)
69+
- DER/PER diff vs previous release
70+
- Asset list with sizes + checksums
71+
72+
```markdown
73+
## rababa_arabic v1.0.0
74+
75+
First production release of the Arabic diacritization student model.
76+
77+
### Metrics
78+
79+
| Metric | This release | Previous |
80+
|---|---|---|
81+
| DER | 4.8% | — |
82+
| p95 latency | 22ms/word | — |
83+
| Size (fp32) | 5.9 MB | — |
84+
| Size (q8) | 1.5 MB | — |
85+
86+
### Training
87+
88+
- Teacher: Qwen3.5-4B-Instruct (LoRA r=16)
89+
- Student: 6M-param char transformer (4 layers, 256 dim)
90+
- Data: Tashkeela++ (1.8M verses) + OpenDiacritizer augmentation
91+
- Wall time: 18h on 1× A100
92+
93+
### Assets
94+
95+
| File | Size | SHA256 |
96+
|---|---|---|
97+
| rababa_arabic.onnx | 5.9 MB | 3a7f...e2b1 |
98+
| rababa_arabic-q8.onnx | 1.5 MB | 8c2d...4f90 |
99+
| ... | ... | ... |
100+
101+
### Known issues
102+
103+
- None
104+
105+
### How to use
106+
107+
\`\`\`js
108+
import { transliterateAsync } from "interscript-ts"
109+
const out = await transliterateAsync("var-ara-Arab-Arab-rababa", "كتب")
110+
\`\`\`
111+
```
112+
113+
## Acceptance
114+
115+
- [ ] `.github/workflows/release.yml` triggers on `*-v*.*.*` tags
116+
- [ ] Release assets include all 8 files above
117+
- [ ] SHA256 sidecars verify against asset bytes
118+
- [ ] Release notes auto-generated with metrics table
119+
- [ ] First release `rababa_arabic-v0.1.0` (pre-training placeholder) shipped

0 commit comments

Comments
 (0)