Models served by htr.spboucher.ai
Each sub-directory is one reconstruction model: models/<id>/ holds the weights (never committed) and a tracked
model_card.json (plus README.md / LICENSE-MODEL.txt for the HTR releases). server.py discovers the
directories at start-up through htr.inference.backbones.ModelRegistry: the model flagged "default": true is
loaded immediately, the others on their first request (at most 2 non-pinned models resident, LRU eviction;
htr-tiny-0.3.0 is pinned).
| id | Name | Family | Parameters | Base model | Default |
|---|---|---|---|---|---|
htr-tiny-0.3.0 |
HTR-Tiny 0.3.0 | htr |
33.5 M | — (paper's confirmatory model M*) | |
htr-base-0.1.0 |
HTR-Base 0.1.0 | htr |
334.8 M | — | |
flan-t5-large-htr |
Flan-T5-Large-HTR | hf-seq2seq |
783 M | google/flan-t5-large | |
qwen2.5-1.5b-htr |
Qwen2.5-1.5B-HTR | hf-causal |
1.54 B | Qwen/Qwen2.5-1.5B | yes |
Families: htr = HTR Transformer trained from scratch (loaded by HTRReconstructor, greedy or beam 3);
hf-seq2seq = Hugging Face encoder–decoder (HFSeq2SeqReconstructor, greedy); hf-causal = Hugging Face
decoder-only LLM (HFCausalReconstructor, greedy). All metrics in the cards are chrF++ differences versus the
identity baseline on the Medium Haiku pairs (exploratory except the confirmatory test number of HTR-Tiny).
model_card.json
{
"id": "qwen2.5-1.5b-htr", // = directory name
"name": "Qwen2.5-1.5B-HTR", // display name
"family": "hf-causal", // htr | hf-seq2seq | hf-causal
"n_params": 1543714304,
"base_model": "Qwen/Qwen2.5-1.5B", // null for htr models
"licence": "…",
"default": true, // exactly one model
"speed_hint": "≈5–15 s per paragraph (greedy, Apple GPU)",
"max_len": 512, // hf-seq2seq: encoder window (tokens)
"max_new_tokens": 768, // hf-causal: generation budget (tokens)
"metrics": {
"reference": "…",
"val_chrf_delta": 2.19, "val_chrf_pm": 0.12,
"test_chrf_delta": 1.97, "test_chrf_pm": 0.06, "test_chrf_ci": null, "test_confirmatory": false,
"heldout_domain_chrf_delta": 1.78, "heldout_domain_pm": 0.13,
"heldout_period_chrf_delta": 1.30, "heldout_period_pm": 0.14,
"number_recall": 0.997, "placeholders_exact": 0.998,
"pangram_human": 48, "pangram_n": 54, "pangram_mixed": 6
},
"notes": ["…"], // backbones: keep the pretraining-corpus caveat
"trained_on": "…",
"exported_at": "2026-11-10"
}Adding a model (no code change)
From the laptop, with the export directory containing the weights and a model_card.json:
scripts/add_model.sh <laptop-model-dir> <id> # e.g. scripts/add_model.sh artifacts/runs/medium_b2_flan_t5_xl/best flan-t5-xl-htrThe script checks the card, rsyncs the directory to m4mm:~/apps/htr/models/<id>/, restarts PM2 htr-web, waits
for /api/models to list the id, and commits the tracked card files on the node. Candidates: flan-t5-xl-htr,
qwen2.5-3b-htr, htr-large. To change the default, set "default": true in exactly one card and restart.
Weights required per family: htr → model.safetensors, config.json, tokenizer.model, release_meta.json;
hf-* → the Hugging Face save_pretrained directory (config.json, model.safetensors, tokenizer files).