textilelabs commited on
Commit
ce3ed47
Β·
verified Β·
1 Parent(s): a87e69d

Upload README.md

Browse files
Files changed (1) hide show
  1. README.md +218 -0
README.md ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ language: en
4
+ library_name: transformers
5
+ pipeline_tag: text-classification
6
+ tags:
7
+ - tiny-model
8
+ - llama
9
+ - from-scratch
10
+ - router
11
+ - tool-use
12
+ - intent-classification
13
+ - agentic
14
+ - gguf
15
+ ---
16
+
17
+ <div align="center">
18
+ <img src="banner.jpg" alt="Loom Router 1" width="520">
19
+ </div>
20
+
21
+ # Loom Router 1
22
+
23
+ <img src="logo.jpg" alt="" width="20" height="20" style="border-radius:4px;vertical-align:middle;margin-right:6px;"> **1.4M parameters Β· 2.8MB Β· Textile Labs**
24
+
25
+ **Give it a user message. It tells you which tool should handle it, in one token.**
26
+
27
+ That's the whole product. Your harness passes the user's original text to whichever tool it
28
+ names β€” the model never rewrites your input, so nothing can be copied wrong or malformed.
29
+
30
+ ```
31
+ "whats the weather in leeds tomorrow" β†’ <route:weather>
32
+ "remind me to call mum at 6" β†’ <route:reminder>
33
+ "whats my sisters name" β†’ <route:unknowable>
34
+ ```
35
+
36
+ **86.5% accuracy on 2,969 real held-out human utterances**, across 17 routes. Random
37
+ guessing scores 5.9%.
38
+
39
+ It is trained **from scratch** β€” randomly initialised weights, trained end to end. Nothing
40
+ is fine-tuned from a pretrained base. Comparable open routers we looked at are considerably
41
+ larger and fine-tuned from pretrained checkpoints; we make no claim to be the smallest of
42
+ its kind.
43
+
44
+ ## What it's for
45
+
46
+ A **first stage in front of a bigger model or an agent loop.** Deciding which tool to reach
47
+ for is a cheap decision that does not need a large model β€” but people usually pay for a
48
+ large model to make it. This does it in one token, on a CPU, in a 2.8MB file.
49
+
50
+ Concretely: use it to pick the tool, then hand the user's original text to that tool. Or use
51
+ it to decide whether you need to call a large model at all.
52
+
53
+ **It is not a chat model.** It has no conversational output and cannot introduce itself. It
54
+ answers with a route and nothing else.
55
+
56
+ ## The routes
57
+
58
+ **Tools (13)** β€” `search` `calc` `time` `weather` `calendar` `reminder` `email` `notes`
59
+ `maps` `translate` `convert` `define` `music`
60
+
61
+ **Control (4)** β€” `answer` `clarify` `unknowable` `refuse`
62
+
63
+ ## The Loom philosophy, as routes
64
+
65
+ Every Loom model is built on the same bet: at small sizes, **knowing your limits is more
66
+ achievable than knowing things β€” and more useful.** In a generative model that means
67
+ saying "I don't know". In a router it becomes something sharper β€” a decision:
68
+
69
+ | route | what it means |
70
+ |---|---|
71
+ | `answer` | no tool needed. Don't reach for one reflexively. |
72
+ | `clarify` | the request is ambiguous. Don't guess β€” ask. |
73
+ | `unknowable` | this depends on something only the user knows. No tool can fix that. |
74
+ | `refuse` | this shouldn't be done. |
75
+
76
+ A router that only answers *"which tool?"* has assumed a tool is always the answer. In an
77
+ agent loop that assumption is the expensive one: sending *"what's my sister's name"* to a
78
+ search tool burns a call and returns a confident wrong answer. `answer` and `clarify` are
79
+ also what let a loop **terminate** instead of spinning.
80
+
81
+ So this card publishes the **false-tool-call rate**: how often it sends a request to a
82
+ tool that cannot possibly help. Ours is **20.2%**, and the honest reading of that is below.
83
+
84
+ ## Measured
85
+
86
+ Evaluated one bare prompt at a time, the way the model is actually used.
87
+
88
+ **Overall 86.5%** Β· tools **89.3%** Β· control **71.2%**
89
+
90
+ | route | n | recall | | route | n | recall |
91
+ |---|---:|---:|---|---|---:|---:|
92
+ | `translate` | 21 | 100.0% | | `email` | 202 | 87.6% |
93
+ | `notes` | 163 | 95.1% | | `reminder` | 134 | 87.3% |
94
+ | `weather` | 113 | 93.8% | | `search` | 599 | 86.8% |
95
+ | `music` | 368 | 93.8% | | `define` | 104 | 84.6% |
96
+ | `answer` | 335 | 93.7% | | `calc` | 32 | 71.9% |
97
+ | `convert` | 64 | 90.6% | | `refuse` | 36 | 25.0% |
98
+ | `time` | 123 | 89.4% | | `clarify` | 41 | 12.2% |
99
+ | `calendar` | 342 | 88.9% | | `unknowable` | 54 | 7.4% |
100
+ | `maps` | 238 | 88.7% | | | | |
101
+
102
+ ## Independent test β€” SNIPS
103
+
104
+ The 86.5% above is a held-out split of the same corpora used for training. To check it
105
+ generalises beyond that, it was also run against **SNIPS**, a dataset that played no part
106
+ in training at all.
107
+
108
+ **73.8% on 500 unseen utterances** (5 intents with an unambiguous mapping):
109
+
110
+ | SNIPS intent | β†’ route | score |
111
+ |---|---|---:|
112
+ | `AddToPlaylist` | `music` | 91% |
113
+ | `PlayMusic` | `music` | 87% |
114
+ | `SearchScreeningEvent` | `search` | 80% |
115
+ | `SearchCreativeWork` | `search` | 68% |
116
+ | `GetWeather` | `weather` | 43% |
117
+
118
+ SNIPS' `BookRestaurant` and `RateBook` have no defensible route in this ontology, so they
119
+ were left unscored rather than graded against a debatable label.
120
+
121
+ The drop from 86.5% to 73.8% is the honest cost of moving to a different data distribution.
122
+ **`GetWeather` at 43% is the instructive failure**: SNIPS asks about weather without using
123
+ the word β€” *"Is there a storm now in NC?"*, *"humidity in Olvey New Hampshire"*, *"Will
124
+ there be fog…"*. Those go to `search`. The model keys on the vocabulary it was trained on,
125
+ not on a general concept of weather. If your domain uses terms outside everyday assistant
126
+ phrasing, expect the same and plan to retrain with them included.
127
+
128
+ ## Read this before relying on it
129
+
130
+ **Tool routing works. The honesty routes largely do not.** `clarify` 12.2%, `unknowable`
131
+ 7.4%, `refuse` 25.0%. Treat a tool prediction as a strong signal and a control prediction
132
+ as a weak hint.
133
+
134
+ The cause is understood and worth stating plainly. On synthetic data those routes scored
135
+ ~76%, because *"my"* and *"I"* were reliable cues. Real assistant traffic is full of *"my
136
+ calendar"*, *"my alarms"*, *"remind me"* β€” so the cue stopped being a cue. The real
137
+ distinction is whether the referent **lives in a tool's data or only in the user's head**,
138
+ which is a subtler thing to learn. Tripling the control training data made it *worse*, so
139
+ it is not a volume problem.
140
+
141
+ `calc` (71.9%) has only 32 validation examples; that figure is noisy.
142
+
143
+ ## Usage β€” Ollama
144
+
145
+ ```bash
146
+ ollama run hf.co/textilelabs/Loom-Router-1 "whats the weather in leeds tomorrow"
147
+ # <route:weather>
148
+ ```
149
+
150
+ Ollama reads the `template` and `params` files in this repo, so there is nothing to set up.
151
+ `params` pins `temperature: 0` and `num_predict: 4` β€” a router should be deterministic and
152
+ emit one token. To build it locally instead: `ollama create loom-router-1 -f Modelfile`.
153
+
154
+ ## Usage β€” transformers
155
+
156
+ ```python
157
+ import torch
158
+ from transformers import AutoTokenizer, AutoModelForCausalLM
159
+
160
+ ROUTES = ["search","calc","time","weather","calendar","reminder","email","notes",
161
+ "maps","translate","convert","define","music","answer","clarify",
162
+ "unknowable","refuse"]
163
+
164
+ tok = AutoTokenizer.from_pretrained("textilelabs/Loom-Router-1")
165
+ model = AutoModelForCausalLM.from_pretrained("textilelabs/Loom-Router-1").eval()
166
+
167
+ route_ids = {tok.convert_tokens_to_ids(f"<route:{r}>"): r for r in ROUTES}
168
+ ids_t = torch.tensor(list(route_ids))
169
+
170
+ def route(message: str) -> str:
171
+ prompt = f"<user>\n{message.strip()}\n<|eot|>\n<loom>\n"
172
+ ids = tok(prompt, return_tensors="pt", add_special_tokens=False).input_ids
173
+ with torch.no_grad():
174
+ logits = model(input_ids=ids).logits[0, -1]
175
+ # Decide only among legal routes, so the output is always a valid label.
176
+ return route_ids[int(ids_t[logits[ids_t].argmax()])]
177
+
178
+ route("add milk to my shopping list") # -> 'notes'
179
+ ```
180
+
181
+ The prompt format is exact: `<user>\n{message}\n<|eot|>\n<loom>\n`, no trailing space.
182
+
183
+ ## In an agent loop
184
+
185
+ ```
186
+ user β†’ router β†’ your harness runs the tool β†’ result β†’ router again
187
+ β†’ 'answer' ends the loop
188
+ ```
189
+
190
+ Cap the number of steps in your harness. `answer` and `clarify` are the terminating routes.
191
+
192
+ ## Files
193
+
194
+ ```
195
+ config.json / model.safetensors the model
196
+ tokenizer.json / tokenizer_config.json custom BPE tokenizer, 2,048 tokens
197
+ loom-router-1-f16.gguf 2.8MB, for Ollama / llama.cpp
198
+ template / params read automatically by `ollama run hf.co/...`
199
+ Modelfile for building locally
200
+ ATTRIBUTION.md required credits for the training corpora
201
+ ```
202
+
203
+ ## Training data
204
+
205
+ Real human utterances from two openly licensed corpora, remapped onto the routes above:
206
+
207
+ - **MASSIVE** β€” Amazon (CC BY 4.0), derived from **SLURP** (CC BY 4.0)
208
+ - **CLINC150** β€” `clinc/oos-eval` (CC BY 3.0)
209
+
210
+ 23,674 real utterances. The four control routes have no public equivalent and are
211
+ procedurally generated. Validation is a held-out split of the *real* utterances β€” never
212
+ templates written by the same process that produced the training data.
213
+
214
+ See `ATTRIBUTION.md`; both licences require credit.
215
+
216
+ ## License
217
+
218
+ Model: MIT. Training data retains its original licences and attribution.