Upload folder using huggingface_hub
Browse files- .gitattributes +2 -35
- README.md +182 -0
- config.json +36 -0
- eval_report.json +86 -0
- pace-intent-router-v8.tinygpt +3 -0
- pace-intent-router-v8.tinygpt.labels.json +13 -0
- prompt.md +50 -0
- tinygpt.lock.json +77 -0
.gitattributes
CHANGED
|
@@ -1,35 +1,2 @@
|
|
| 1 |
-
|
| 2 |
-
*.
|
| 3 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
-
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
-
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
-
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
-
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
-
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
-
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
-
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
-
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
-
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
-
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
-
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
-
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
-
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
-
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
-
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
-
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
-
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
-
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
-
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
-
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
| 1 |
+
pace-intent-router-v8.tinygpt filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.tinygpt filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
ADDED
|
@@ -0,0 +1,182 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
base_model: posttrainllm-ToolRouterModel-small
|
| 3 |
+
language:
|
| 4 |
+
- en
|
| 5 |
+
library_name: posttrainllm
|
| 6 |
+
license: mit
|
| 7 |
+
pipeline_tag: text-classification
|
| 8 |
+
tags:
|
| 9 |
+
- posttrainllm
|
| 10 |
+
- tinygpt
|
| 11 |
+
- intent-classification
|
| 12 |
+
- routing
|
| 13 |
+
- pace
|
| 14 |
+
- mac-first
|
| 15 |
+
- on-device
|
| 16 |
+
---
|
| 17 |
+
|
| 18 |
+
# Pace Intent Router v8
|
| 19 |
+
|
| 20 |
+
## Summary
|
| 21 |
+
|
| 22 |
+
A 49.5M-parameter intent classifier trained from scratch on a 127K-example
|
| 23 |
+
synthetic corpus for Pace's 7-class intent taxonomy. Runs in 3ms on CPU,
|
| 24 |
+
beats Qwen3-4B-Instruct by 10.8 percentage points, and is 85x faster.
|
| 25 |
+
|
| 26 |
+
This is a **posttrainllm-built specialist** — trained with the
|
| 27 |
+
`train-extractor` command on a `ToolRouterModel` small preset (6 layers,
|
| 28 |
+
384 d_model, byte-level vocab 256). No pre-trained weights, no
|
| 29 |
+
distillation from a teacher. The model learns Pace-specific decision
|
| 30 |
+
boundaries from synthetic data generated by
|
| 31 |
+
`pace/scripts/generate-intent-corpus-v2.py`.
|
| 32 |
+
|
| 33 |
+
## Artifact
|
| 34 |
+
|
| 35 |
+
- Package id: `pace-intent-router-v8`
|
| 36 |
+
- Artifact: `runs/pace-intent-router-v8.tinygpt` (566 MB)
|
| 37 |
+
- Labels: `runs/pace-intent-router-v8.tinygpt.labels.json`
|
| 38 |
+
- Format: posttrainllm `.tinygpt` (ToolRouterModel checkpoint)
|
| 39 |
+
- Base: `posttrainllm-ToolRouterModel-small` (trained from scratch)
|
| 40 |
+
- Params: 49.5M (6 layers, 384 d_model, 7 classes)
|
| 41 |
+
- Training method: `train-extractor` (cross-entropy classification,
|
| 42 |
+
AdamW + cosine decay, 18000 steps on v5 data)
|
| 43 |
+
- Training time: ~30 minutes on M5 Pro
|
| 44 |
+
|
| 45 |
+
## Measured Result
|
| 46 |
+
|
| 47 |
+
### Head-to-head vs Qwen3-4B-Instruct (4-bit)
|
| 48 |
+
|
| 49 |
+
| Metric | ToolRouterModel v8 | Qwen3-4B-Instruct | Delta |
|
| 50 |
+
|---|---:|---:|---:|
|
| 51 |
+
| Overall accuracy | **95.5%** | 84.75% | **+10.8 pp** |
|
| 52 |
+
| Eval size | 14,995 | 997 (stratified sample) | — |
|
| 53 |
+
| Latency p50 | **3.1 ms** | 240 ms | **77x faster** |
|
| 54 |
+
| Model size | **566 MB** | ~2,300 MB | **4x smaller** |
|
| 55 |
+
| Params | 49.5M | 4B (4-bit) | — |
|
| 56 |
+
|
| 57 |
+
### Per-class accuracy (v8 vs v5 vs Qwen3-4B)
|
| 58 |
+
|
| 59 |
+
| Class | v8 (49.5M) | v5 (49.5M) | Qwen3-4B |
|
| 60 |
+
|---|---:|---:|---:|
|
| 61 |
+
| chitchat | 93.8% | **95.6%** | 78.5% |
|
| 62 |
+
| pureKnowledge | 96.4% | **97.0%** | 83.0% |
|
| 63 |
+
| screenDescription | **97.6%** | 97.1% | **95.8%** |
|
| 64 |
+
| screenAction | 96.8% | **97.4%** | 91.0% |
|
| 65 |
+
| research | 93.4% | **97.1%** | **100.0%** |
|
| 66 |
+
| phoneLargeModel | **98.4%** | 96.4% | 76.7% |
|
| 67 |
+
| unknown | **79.6%** | 70.3% | 2.6% |
|
| 68 |
+
| **Overall** | 95.5% | **95.9%** | 84.75% |
|
| 69 |
+
|
| 70 |
+
### Key takeaways
|
| 71 |
+
|
| 72 |
+
1. **+10.8 pp over Qwen3-4B**: the 49.5M specialist beats the 4B
|
| 73 |
+
generalist on this task. Intent classification is a narrow enough
|
| 74 |
+
problem that a from-scratch model outperforms a general LLM.
|
| 75 |
+
2. **77x faster**: 3.1ms vs 240ms. The router runs in sub-4ms on CPU;
|
| 76 |
+
Qwen needs a full LLM forward pass.
|
| 77 |
+
3. **Unknown class is the moat**: v8 scores 79.6% on unknown vs Qwen's
|
| 78 |
+
2.6%. Qwen almost never returns "unknown" — it forces everything
|
| 79 |
+
into a known class, which is risky for a router (wrong route →
|
| 80 |
+
wrong pipeline). The trained model knows when to say "I don't know."
|
| 81 |
+
4. **v5 vs v8**: v5 (12K steps) slightly outperforms v8 (18K steps)
|
| 82 |
+
overall (95.9% vs 95.5%), but v8 has a much better unknown class
|
| 83 |
+
(79.6% vs 70.3%, +9.3 pp). The longer training improved the hardest
|
| 84 |
+
class at a small cost to the easier ones.
|
| 85 |
+
|
| 86 |
+
## Training data
|
| 87 |
+
|
| 88 |
+
- Dataset: pace-intent-corpus-v2 (fully synthetic)
|
| 89 |
+
- Source: `pace/scripts/generate-intent-corpus-v2.py` (base corpus,
|
| 90 |
+
combinatorial expansion) + `pace/scripts/generate-intent-supplement-v2.py`
|
| 91 |
+
(targeted weak-spot supplement)
|
| 92 |
+
- Train rows: 112,061
|
| 93 |
+
- Heldout rows: 14,995
|
| 94 |
+
- Classes: 7 (chitchat, pureKnowledge, screenDescription, screenAction,
|
| 95 |
+
research, phoneLargeModel, unknown)
|
| 96 |
+
- Split: stratified 85/15
|
| 97 |
+
- No real user data — all synthetic
|
| 98 |
+
|
| 99 |
+
### Why synthetic data works here
|
| 100 |
+
|
| 101 |
+
The synthetic corpus encodes Pace-specific decision boundaries that a
|
| 102 |
+
general LLM doesn't know:
|
| 103 |
+
- "turn on lights" = unknown (Pace can't control lights)
|
| 104 |
+
- "turn on volume" = screenAction (Pace can control volume)
|
| 105 |
+
- "what can you do" = pureKnowledge (not unknown — it's a question
|
| 106 |
+
about Pace itself)
|
| 107 |
+
|
| 108 |
+
These boundaries are product-specific, not language-general. A
|
| 109 |
+
from-scratch model learns them directly from the corpus; a general LLM
|
| 110 |
+
needs few-shot examples or fine-tuning to learn them.
|
| 111 |
+
|
| 112 |
+
## Recommended Use
|
| 113 |
+
|
| 114 |
+
This model is a **router**, not a planner. It classifies a user
|
| 115 |
+
utterance into one of 7 intent classes in 3ms. The intent class
|
| 116 |
+
determines which pipeline handles the turn:
|
| 117 |
+
|
| 118 |
+
| Intent | Route | Model |
|
| 119 |
+
|---|---|---|
|
| 120 |
+
| chitchat | fast path | Apple FM / local text-only |
|
| 121 |
+
| pureKnowledge | answer directly | Apple FM / local text-only |
|
| 122 |
+
| screenDescription | read screen | local planner + VLM |
|
| 123 |
+
| screenAction | execute tool | local planner + action layer |
|
| 124 |
+
| research | research tier | codex CLI / Claude CLI |
|
| 125 |
+
| phoneLargeModel | cloud bridge | codex CLI / cloud bridge |
|
| 126 |
+
| unknown | full pipeline | local planner (best-effort) |
|
| 127 |
+
|
| 128 |
+
Do not use this model for response generation. It only classifies.
|
| 129 |
+
|
| 130 |
+
## Known Limits
|
| 131 |
+
|
| 132 |
+
- **Unknown class is synthetic**: the 79.6% unknown accuracy is measured
|
| 133 |
+
on synthetic unknowns. Real-world unknown distribution will differ.
|
| 134 |
+
The model needs real-world unknown examples to improve further.
|
| 135 |
+
- **Not wired into Pace**: the shipping app uses Apple FM for intent
|
| 136 |
+
classification (when available) and rule-based fallback. This model
|
| 137 |
+
is a training pipeline artifact, not a product component.
|
| 138 |
+
- **Byte-level vocab**: the model uses a 256-token byte-level vocabulary.
|
| 139 |
+
Longer queries are truncated at 128 bytes. A BPE tokenizer would
|
| 140 |
+
generalize better on longer queries.
|
| 141 |
+
- **No real user data**: all training data is synthetic. Real user
|
| 142 |
+
queries will have different phrasings, accents, and edge cases.
|
| 143 |
+
|
| 144 |
+
## Why this model exists
|
| 145 |
+
|
| 146 |
+
The Pace intent classifier was originally rule-based (hand-written
|
| 147 |
+
phrase lists). This model was trained to test whether a learned
|
| 148 |
+
classifier could beat the rules. It can — 95.5% vs the rules' ~95% on
|
| 149 |
+
the synthetic eval — and it also beats Apple Foundation Models (3B,
|
| 150 |
+
in-process) on the same task: 95.5% vs 76.5% on a 200-example
|
| 151 |
+
stratified sample. The model is most useful as:
|
| 152 |
+
|
| 153 |
+
1. **The production intent classifier** — it's faster (3ms vs 1600ms),
|
| 154 |
+
more accurate (95.5% vs 76.5%), and has calibrated confidence
|
| 155 |
+
(real softmax vs hardcoded 0.95)
|
| 156 |
+
2. A fallback when Apple FM is unavailable (older Macs)
|
| 157 |
+
3. A training pipeline validation (the corpus and pipeline are assets)
|
| 158 |
+
4. A baseline for future on-device classifiers
|
| 159 |
+
|
| 160 |
+
### Measured head-to-head (200 stratified examples, 1s delay)
|
| 161 |
+
|
| 162 |
+
| Model | Accuracy | Latency | Error rate |
|
| 163 |
+
|---|---|---|---|
|
| 164 |
+
| **TinyGPT v8 (49.5M)** | **95.5%** | **3.1ms p50** | 0% |
|
| 165 |
+
| Qwen3-4B-Instruct (4-bit) | 84.75% | 240ms p50 | 0% |
|
| 166 |
+
| Apple FM (3B, in-process) | 76.5% | 1597ms mean | 0.5% |
|
| 167 |
+
|
| 168 |
+
FM's main weakness: it conflates `research` with `pureKnowledge`
|
| 169 |
+
(33.3% vs 93.4% — a 60pp gap) because it doesn't understand the
|
| 170 |
+
Pace-specific distinction between "research X" (multi-step) and
|
| 171 |
+
"what is X" (single answer). FM also refused to answer one query
|
| 172 |
+
("model refused to answer").
|
| 173 |
+
|
| 174 |
+
## References
|
| 175 |
+
|
| 176 |
+
- Factory run report: `runs/2026-07-13-pace-intent-router-v1/report.md`
|
| 177 |
+
- Eval data: `runs/2026-07-13-pace-intent-router-v1/eval-candidate-v8.json`
|
| 178 |
+
- Head-to-head: `runs/2026-07-13-pace-intent-router-v1/head-to-head.json`
|
| 179 |
+
- Training config: `runs/2026-07-13-pace-intent-router-v1/config.json`
|
| 180 |
+
- Corpus generator: `pace/scripts/generate-intent-corpus-v2.py`
|
| 181 |
+
- Supplement generator: `pace/scripts/generate-intent-supplement-v2.py`
|
| 182 |
+
- Pace routing architecture: `pace/leanring-buddy/PaceIntentClassifier.swift`
|
config.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model_type": "tool_router",
|
| 3 |
+
"architecture": "ToolRouterModel",
|
| 4 |
+
"preset": "small",
|
| 5 |
+
"layers": 6,
|
| 6 |
+
"d_model": 384,
|
| 7 |
+
"vocab_size": 256,
|
| 8 |
+
"context_length": 128,
|
| 9 |
+
"num_labels": 7,
|
| 10 |
+
"labels": [
|
| 11 |
+
"chitchat",
|
| 12 |
+
"phoneLargeModel",
|
| 13 |
+
"pureKnowledge",
|
| 14 |
+
"research",
|
| 15 |
+
"screenAction",
|
| 16 |
+
"screenDescription",
|
| 17 |
+
"unknown"
|
| 18 |
+
],
|
| 19 |
+
"precision": "float32",
|
| 20 |
+
"params": 49500000,
|
| 21 |
+
"training_method": "train-extractor",
|
| 22 |
+
"framework": "posttrainllm",
|
| 23 |
+
"library_name": "posttrainllm",
|
| 24 |
+
"license": "mit",
|
| 25 |
+
"language": ["en"],
|
| 26 |
+
"pipeline_tag": "text-classification",
|
| 27 |
+
"tags": [
|
| 28 |
+
"posttrainllm",
|
| 29 |
+
"tinygpt",
|
| 30 |
+
"intent-classification",
|
| 31 |
+
"routing",
|
| 32 |
+
"pace",
|
| 33 |
+
"mac-first",
|
| 34 |
+
"on-device"
|
| 35 |
+
]
|
| 36 |
+
}
|
eval_report.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"model": "pace-intent-router-v8",
|
| 3 |
+
"eval_date": "2026-07-13",
|
| 4 |
+
"eval_split": "pace-intent-heldout-v5",
|
| 5 |
+
"eval_size": 14995,
|
| 6 |
+
"overall_accuracy": 0.9552517505835278,
|
| 7 |
+
"latency_ms": {
|
| 8 |
+
"p50": 3.1020641326904297,
|
| 9 |
+
"mean": 3.2842456757843435
|
| 10 |
+
},
|
| 11 |
+
"per_class": {
|
| 12 |
+
"chitchat": {
|
| 13 |
+
"accuracy": 0.9381672597864769,
|
| 14 |
+
"count": 2248
|
| 15 |
+
},
|
| 16 |
+
"phoneLargeModel": {
|
| 17 |
+
"accuracy": 0.9844444444444445,
|
| 18 |
+
"count": 450
|
| 19 |
+
},
|
| 20 |
+
"pureKnowledge": {
|
| 21 |
+
"accuracy": 0.964,
|
| 22 |
+
"count": 3000
|
| 23 |
+
},
|
| 24 |
+
"research": {
|
| 25 |
+
"accuracy": 0.9344444444444444,
|
| 26 |
+
"count": 900
|
| 27 |
+
},
|
| 28 |
+
"screenAction": {
|
| 29 |
+
"accuracy": 0.9678279713285548,
|
| 30 |
+
"count": 5999
|
| 31 |
+
},
|
| 32 |
+
"screenDescription": {
|
| 33 |
+
"accuracy": 0.9760978321289605,
|
| 34 |
+
"count": 1799
|
| 35 |
+
},
|
| 36 |
+
"unknown": {
|
| 37 |
+
"accuracy": 0.7963272120200334,
|
| 38 |
+
"count": 599
|
| 39 |
+
}
|
| 40 |
+
},
|
| 41 |
+
"baselines": {
|
| 42 |
+
"apple-fm-3b": {
|
| 43 |
+
"overall_accuracy": 0.765,
|
| 44 |
+
"eval_size": 200,
|
| 45 |
+
"latency_mean_ms": 1597,
|
| 46 |
+
"error_rate": 0.005,
|
| 47 |
+
"source": "pace/leanring-buddyTests/PaceFMIntentClassifierBenchmarkTests.swift",
|
| 48 |
+
"notes": "200 stratified examples, 1s delay between calls, fresh session per call. FM conflates research with pureKnowledge (33.3% vs 93.4%). One refusal."
|
| 49 |
+
},
|
| 50 |
+
"qwen3-4b-instruct-4bit": {
|
| 51 |
+
"overall_accuracy": 0.847542627883651,
|
| 52 |
+
"eval_size": 997,
|
| 53 |
+
"latency_p50_ms": 240,
|
| 54 |
+
"source": "runs/2026-07-13-pace-intent-router-v1/eval-qwen-baseline.json"
|
| 55 |
+
},
|
| 56 |
+
"pace-intent-router-v5": {
|
| 57 |
+
"overall_accuracy": 0.9590530176725576,
|
| 58 |
+
"eval_size": 14995,
|
| 59 |
+
"latency_p50_ms": 4.642009735107422,
|
| 60 |
+
"source": "runs/2026-07-13-pace-intent-router-v1/eval-candidate-v5.json"
|
| 61 |
+
}
|
| 62 |
+
},
|
| 63 |
+
"head_to_head": {
|
| 64 |
+
"vs_apple_fm": {
|
| 65 |
+
"accuracy_delta_pp": 19.0,
|
| 66 |
+
"latency_speedup": "515x",
|
| 67 |
+
"note": "FM is 3B in-process but beats it on every class. FM conflates research with pureKnowledge."
|
| 68 |
+
},
|
| 69 |
+
"vs_qwen3_4b": {
|
| 70 |
+
"accuracy_delta_pp": 10.77,
|
| 71 |
+
"latency_speedup": "77x",
|
| 72 |
+
"size_ratio": "4x smaller"
|
| 73 |
+
},
|
| 74 |
+
"vs_v5": {
|
| 75 |
+
"accuracy_delta_pp": -0.38,
|
| 76 |
+
"unknown_class_delta_pp": 9.35,
|
| 77 |
+
"note": "v8 trades slight overall accuracy for significantly better unknown-class recall"
|
| 78 |
+
}
|
| 79 |
+
},
|
| 80 |
+
"caveats": [
|
| 81 |
+
"All eval data is synthetic — real-world distribution will differ",
|
| 82 |
+
"Unknown class accuracy (79.6%) is measured on synthetic unknowns",
|
| 83 |
+
"Not wired into shipping Pace app yet — Apple FM is the current production classifier despite lower measured accuracy (76.5% vs 95.5%)",
|
| 84 |
+
"Byte-level vocab truncates queries at 128 bytes"
|
| 85 |
+
]
|
| 86 |
+
}
|
pace-intent-router-v8.tinygpt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:adf28f05f3e93960989f7b2417c0dda62210be775b2d517a4b99b59966a98a9e
|
| 3 |
+
size 593849532
|
pace-intent-router-v8.tinygpt.labels.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"kind" : "tool-router",
|
| 3 |
+
"labels" : [
|
| 4 |
+
"chitchat",
|
| 5 |
+
"phoneLargeModel",
|
| 6 |
+
"pureKnowledge",
|
| 7 |
+
"research",
|
| 8 |
+
"screenAction",
|
| 9 |
+
"screenDescription",
|
| 10 |
+
"unknown"
|
| 11 |
+
],
|
| 12 |
+
"version" : 1
|
| 13 |
+
}
|
prompt.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Pace Intent Router — classification prompt
|
| 2 |
+
|
| 3 |
+
This model is a classifier, not a generative model. It does not use a
|
| 4 |
+
prompt in the traditional sense. The input is a raw user transcript
|
| 5 |
+
(byte-level encoded, max 128 bytes) and the output is one of 7 class
|
| 6 |
+
labels.
|
| 7 |
+
|
| 8 |
+
## Input format
|
| 9 |
+
|
| 10 |
+
```
|
| 11 |
+
<raw user transcript, byte-level encoded, max 128 bytes>
|
| 12 |
+
```
|
| 13 |
+
|
| 14 |
+
## Output format
|
| 15 |
+
|
| 16 |
+
```
|
| 17 |
+
<single class label from: chitchat, pureKnowledge, screenDescription,
|
| 18 |
+
screenAction, research, phoneLargeModel, unknown>
|
| 19 |
+
```
|
| 20 |
+
|
| 21 |
+
## Class definitions
|
| 22 |
+
|
| 23 |
+
- **chitchat**: Greetings, thanks, goodbyes, social filler. e.g. "hi
|
| 24 |
+
pace", "thanks", "how are you"
|
| 25 |
+
- **pureKnowledge**: Factual questions answerable without screen
|
| 26 |
+
context. e.g. "what is HTML", "explain DNS"
|
| 27 |
+
- **screenDescription**: User wants a description of what's on screen.
|
| 28 |
+
e.g. "what am I looking at", "what's on the screen"
|
| 29 |
+
- **screenAction**: User wants Pace to do something via the action
|
| 30 |
+
layer. e.g. "click the save button", "open Safari"
|
| 31 |
+
- **research**: Multi-step research turn. e.g. "research quantum
|
| 32 |
+
computing", "compare AWS vs GCP"
|
| 33 |
+
- **phoneLargeModel**: Explicit escalation request. e.g. "use the big
|
| 34 |
+
model", "phone a large model"
|
| 35 |
+
- **unknown**: Classifier could not confidently assign a class. The
|
| 36 |
+
caller must run the full pipeline.
|
| 37 |
+
|
| 38 |
+
## Decision boundaries (Pace-specific)
|
| 39 |
+
|
| 40 |
+
These boundaries are encoded in the synthetic training corpus and are
|
| 41 |
+
what the model learns. A general LLM does not know these:
|
| 42 |
+
|
| 43 |
+
- "turn on lights" = **unknown** (Pace can't control lights)
|
| 44 |
+
- "turn on volume" = **screenAction** (Pace can control volume)
|
| 45 |
+
- "what can you do" = **pureKnowledge** (not unknown — it's a question
|
| 46 |
+
about Pace itself)
|
| 47 |
+
- "research HTML" = **research** (not pureKnowledge — the word
|
| 48 |
+
"research" triggers the research lane)
|
| 49 |
+
- "I researched HTML yesterday" = **pureKnowledge** (past tense — not
|
| 50 |
+
a research request)
|
tinygpt.lock.json
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"schema": "posttrainllm.specialist.lock.v1",
|
| 3 |
+
"id": "pace-intent-router-v8",
|
| 4 |
+
"artifact_type": "tinygpt_checkpoint",
|
| 5 |
+
"local_path": "runs/pace-intent-router-v8.tinygpt",
|
| 6 |
+
"public_uri": null,
|
| 7 |
+
"storage": {
|
| 8 |
+
"primary": "local_runs_directory",
|
| 9 |
+
"status": "local-only",
|
| 10 |
+
"large_weights_published": false,
|
| 11 |
+
"r2": "not-applicable"
|
| 12 |
+
},
|
| 13 |
+
"base_model": {
|
| 14 |
+
"id": "posttrainllm-ToolRouterModel-small",
|
| 15 |
+
"preset": "small",
|
| 16 |
+
"architecture": "ToolRouterModel",
|
| 17 |
+
"precision": "float32",
|
| 18 |
+
"layers": 6,
|
| 19 |
+
"d_model": 384,
|
| 20 |
+
"vocab_size": 256,
|
| 21 |
+
"context_length": 128
|
| 22 |
+
},
|
| 23 |
+
"compatibility": {
|
| 24 |
+
"posttrainllm_serve": true,
|
| 25 |
+
"posttrainllm_sample": true,
|
| 26 |
+
"mlx_lm": false,
|
| 27 |
+
"browser_playground": false,
|
| 28 |
+
"pace_app_integration": false
|
| 29 |
+
},
|
| 30 |
+
"files": [
|
| 31 |
+
{
|
| 32 |
+
"path": "runs/pace-intent-router-v8.tinygpt",
|
| 33 |
+
"bytes": 593498112,
|
| 34 |
+
"role": "model_checkpoint"
|
| 35 |
+
},
|
| 36 |
+
{
|
| 37 |
+
"path": "runs/pace-intent-router-v8.tinygpt.labels.json",
|
| 38 |
+
"bytes": 200,
|
| 39 |
+
"role": "label_map"
|
| 40 |
+
}
|
| 41 |
+
],
|
| 42 |
+
"training": {
|
| 43 |
+
"method": "train-extractor",
|
| 44 |
+
"steps": 18000,
|
| 45 |
+
"batch_size": 32,
|
| 46 |
+
"learning_rate": 0.0003,
|
| 47 |
+
"warmup_steps": 100,
|
| 48 |
+
"val_split": 0.05,
|
| 49 |
+
"val_every": 100,
|
| 50 |
+
"seed": 42,
|
| 51 |
+
"train_rows": 112061,
|
| 52 |
+
"heldout_rows": 14995,
|
| 53 |
+
"classes": [
|
| 54 |
+
"chitchat",
|
| 55 |
+
"phoneLargeModel",
|
| 56 |
+
"pureKnowledge",
|
| 57 |
+
"research",
|
| 58 |
+
"screenAction",
|
| 59 |
+
"screenDescription",
|
| 60 |
+
"unknown"
|
| 61 |
+
],
|
| 62 |
+
"data_source": "pace/scripts/generate-intent-corpus-v2.py + generate-intent-supplement-v2.py",
|
| 63 |
+
"data_type": "fully_synthetic"
|
| 64 |
+
},
|
| 65 |
+
"package_files": {
|
| 66 |
+
"model_card": "model_card.md",
|
| 67 |
+
"prompt": "prompt.md",
|
| 68 |
+
"eval_report": "eval_report.json",
|
| 69 |
+
"loader": "mlx_load.py"
|
| 70 |
+
},
|
| 71 |
+
"notes": [
|
| 72 |
+
"Local-only artifact — not published to Hugging Face Hub.",
|
| 73 |
+
"Use posttrainllm serve or posttrainllm sample to run inference.",
|
| 74 |
+
"Not wired into the Pace shipping app — Apple FM is the production classifier.",
|
| 75 |
+
"The v5 checkpoint (12000 steps) slightly outperforms v8 on overall accuracy but v8 has better unknown-class recall."
|
| 76 |
+
]
|
| 77 |
+
}
|