textilelabs commited on
Commit
1732e7e
Β·
verified Β·
1 Parent(s): 86a4f93

Upload 3 files

Browse files
Files changed (3) hide show
  1. Modelfile +14 -0
  2. README.md +247 -0
  3. params +7 -0
Modelfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ./loom-tapestry-2-f16.gguf
2
+ TEMPLATE """<tools:off>
3
+ <user>
4
+ {{ .Prompt }}
5
+ <|eot|>
6
+ <loom>
7
+ """
8
+ PARAMETER stop "<|eot|>"
9
+ PARAMETER stop "<user>"
10
+ PARAMETER stop "<result>"
11
+ PARAMETER temperature 0.7
12
+ PARAMETER top_k 40
13
+ PARAMETER repeat_penalty 1.0
14
+ PARAMETER num_predict 96
README.md ADDED
@@ -0,0 +1,247 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: mit
3
+ language: en
4
+ library_name: transformers
5
+ pipeline_tag: text-generation
6
+ tags:
7
+ - tiny-model
8
+ - llama
9
+ - from-scratch
10
+ - conversational
11
+ - tool-use
12
+ - agent-harness
13
+ - retrieval-augmented
14
+ - attribution
15
+ - calibrated-honesty
16
+ - humble-ai
17
+ - philosophy-of-mind
18
+ - small-language-model
19
+ - cpu-trained
20
+ - muon
21
+ - gguf
22
+ - text-generation-inference
23
+ widget:
24
+ - text: "<tools:off>\n<user>\nwho are you\n<|eot|>\n<loom>\n"
25
+ example_title: "Identity"
26
+ - text: "<tools:off>\n<user>\nwhat is my sisters name\n<|eot|>\n<loom>\n"
27
+ example_title: "Limits"
28
+ - text: "<tools:on>\n<user>\nwhat is the capital of peru\n<|eot|>\n<loom>\n"
29
+ example_title: "Lookup"
30
+ ---
31
+
32
+ <div align="center">
33
+ <img src="banner.jpg" alt="Loom Tapestry 2" width="520">
34
+ </div>
35
+
36
+ # Loom Tapestry 2
37
+
38
+ <img src="logo.jpg" alt="" width="20" height="20" style="border-radius:4px;vertical-align:middle;margin-right:6px;"> **22.8M parameters Β· 20 layers Β· 768 context Β· Textile Labs**
39
+
40
+ The first model in the **Tapestry** tier. Loom tiers run **Flash β†’ Spark β†’ Weave β†’ Tapestry**.
41
+
42
+ Trained from scratch on a 2013 desktop CPU β€” randomly initialised weights, nothing
43
+ fine-tuned from a pretrained base.
44
+
45
+ **It looks things up, and it tells you when it did.**
46
+
47
+ ```
48
+ who are you β†’ Loom Tapestry 2, a small model by Textile Labs.
49
+ what is my sisters name β†’ I don't know that about you.
50
+
51
+ what is the capital of peru
52
+ β†’ <lookup>what is the capital of peru</lookup>
53
+ ← Lima is the capital and largest city of Peru…
54
+ β†’ Lima is the capital and largest city of Peru. I had to look that up.
55
+ how many people live there
56
+ β†’ 9.7 million. ← same result, no second lookup
57
+ ```
58
+
59
+ ## Why "I looked that up" matters
60
+
61
+ Most small models make you guess which of their answers to trust. Tapestry has three
62
+ honest registers, and you can tell them apart **by reading**:
63
+
64
+ | situation | what it does |
65
+ |---|---|
66
+ | answered from a retrieved `<result>` | **says it looked it up** |
67
+ | answered from training | answers plainly |
68
+ | cannot be known | *"I don't know that about you."* |
69
+
70
+ It never claims a lookup it didn't make β€” **16/16** on that check below. A false
71
+ attribution would be worse than none, so that is the one number held to 100%.
72
+
73
+ ## Measured
74
+
75
+ Full acceptance battery, hand-written prompts held out of the training generator,
76
+ scored on **content** rather than shape. Every failure is listed rather than summarised.
77
+
78
+ | | score | |
79
+ |---|---:|---|
80
+ | no false attribution | **16/16** | never claims a lookup it didn't make |
81
+ | no `<lookup>` leak with tools off | **28/28** | |
82
+ | self-terminates without a Modelfile | **12/12** | |
83
+ | answers from a supplied `<result>` | **5/5** | |
84
+ | identity β€” names Tapestry | **11/12** | |
85
+ | attribution present after a real lookup | **4/5** | |
86
+ | identity under CAPS / typos / "?" | **10/12** | |
87
+ | 5-turn conversation stays on thread | **4/5** | |
88
+ | admits an unknowable | **4/8** | |
89
+ | follow-up answered from the same result | **2/5** | |
90
+ | says the result doesn't contain it | **1/5** | first Loom to score above zero |
91
+ | tool decision with tools **on** | **10/20** | 10/10 correct when a lookup *is* needed; **0/10** when it is not β€” see below |
92
+ | **overall** | **107/133 Β· 80.5%** | |
93
+
94
+ ### Against the previous generation
95
+
96
+ Identical corpus family, same evaluation method.
97
+
98
+ | | params | val loss | val accuracy |
99
+ |---|---:|---:|---:|
100
+ | Loom Spark 2 | 19.9M | 2.692 | 0.536 |
101
+ | Loom Weave 2 Flash | 19.9M | 2.254 | 0.580 |
102
+ | **Loom Tapestry 2** | **22.8M** | **1.963** | **0.622** |
103
+
104
+ 13% lower loss and +4.2 accuracy points over the previous best.
105
+
106
+ ## Read this before you use it
107
+
108
+ **Keep tools OFF for conversation.** The persona was trained entirely under `tools:off`.
109
+ With tools **on**, identity and personal questions get turned into a lookup β€” measured
110
+ **0/10** on that case. The shipped Ollama template defaults to `tools:off`; switch to
111
+ `tools:on` only for the retrieval loop.
112
+
113
+ **Validate what it tells you from a result.** It answers from a `<result>` whether or not
114
+ the answer is actually in there β€” "says the result doesn't contain it" is 1/5. Treat the
115
+ retrieved text as the trustworthy part and the model's summary of it as unreliable.
116
+ Extraction picks the wrong span roughly a third of the time.
117
+
118
+ **It is a lookup assistant, not a chat companion.** At 22.8M parameters it does not
119
+ improvise, explain in its own words, or hold a free-ranging conversation. What it does
120
+ reliably is decide a lookup is needed, write the query, read the answer back, and say
121
+ where the answer came from.
122
+
123
+ **It has almost no world knowledge.** With tools off it declines factual questions. That is
124
+ the intended behaviour, not a fault.
125
+
126
+ ## Two modes
127
+
128
+ **`<tools:off>` (default)** β€” conversational. Identity, limits, warmth, brevity.
129
+
130
+ **`<tools:on>`** β€” emits `<lookup>query</lookup>` and stops. Your harness runs the lookup
131
+ and continues with a `<result>` block:
132
+
133
+ ```
134
+ <tools:on>
135
+ <user>
136
+ what is the capital of peru
137
+ <|eot|>
138
+ <loom>
139
+ <lookup>what is the capital of peru</lookup><|eot|>
140
+ <result>
141
+ Lima is the capital and largest city of Peru.
142
+ <|eot|>
143
+ <loom>
144
+ ```
145
+
146
+ ## Usage β€” the harness
147
+
148
+ `harness.py` in this repo runs the lookup and feeds the result back. Wikipedia is used
149
+ because it is free and needs no key β€” swap the `search()` function for anything else; the
150
+ contract is text in, text out.
151
+
152
+ ```bash
153
+ python3 harness.py "who wrote dracula" # with lookups
154
+ python3 harness.py # interactive
155
+ python3 harness.py --no-tools "who are you" # chat only
156
+ ```
157
+
158
+ Three things any harness for this model needs:
159
+
160
+ - **Never feed a failed lookup back as a `<result>`.** It will earnestly answer from the
161
+ error text. Fail loudly instead β€” `harness.py` does.
162
+ - Wikipedia returns **403** without a descriptive `User-Agent`.
163
+ - macOS system Python often needs **certifi** for TLS.
164
+
165
+ ## Usage β€” Ollama
166
+
167
+ ```bash
168
+ ollama run hf.co/textilelabs/Loom-Tapestry-2 "who are you"
169
+ ```
170
+
171
+ The `template` and `params` files in this repo are read automatically. To build locally:
172
+ `ollama create loom-tapestry-2 -f Modelfile`.
173
+
174
+ **Do not add a repetition penalty.** This model answers by quoting from the `<result>` you
175
+ give it, so penalising repeated tokens penalises the correct answer. Measured at
176
+ `repeat_penalty 1.15` it changed "1,345 metres" into "2,345 metres" β€” silently wrong rather
177
+ than merely worse. `params` ships it at 1.0 for that reason. The trade-off is that on a
178
+ question it cannot handle it will occasionally loop on a short phrase until it hits
179
+ `num_predict`; that is the safer failure.
180
+
181
+ ## Usage β€” transformers
182
+
183
+ ```python
184
+ import torch
185
+ from transformers import AutoTokenizer, AutoModelForCausalLM
186
+
187
+ tok = AutoTokenizer.from_pretrained("textilelabs/Loom-Tapestry-2")
188
+ model = AutoModelForCausalLM.from_pretrained("textilelabs/Loom-Tapestry-2").eval()
189
+ eot = tok.convert_tokens_to_ids("<|eot|>")
190
+
191
+ def ask(message, tools=False):
192
+ p = f"<tools:{'on' if tools else 'off'}>\n<user>\n{message}\n<|eot|>\n<loom>\n"
193
+ ids = tok(p, return_tensors="pt", add_special_tokens=False).input_ids
194
+ with torch.no_grad():
195
+ out = model.generate(ids, max_new_tokens=64, do_sample=False, eos_token_id=eot,
196
+ pad_token_id=tok.convert_tokens_to_ids("<|pad|>"))[0]
197
+ return tok.decode(out[ids.shape[1]:], skip_special_tokens=True).strip()
198
+
199
+ ask("who are you") # -> 'Loom Tapestry 2, a small model by Textile Labs.'
200
+ ```
201
+
202
+ Prompt format is exact: `<tools:off>\n<user>\n{message}\n<|eot|>\n<loom>\n`.
203
+ No trailing space after `<loom>`.
204
+
205
+ ## How it was built
206
+
207
+ | | |
208
+ |---|---|
209
+ | architecture | Llama β€” 20 layers Γ— 320d, GQA, SwiGLU, RoPE, tied embeddings |
210
+ | context | 768 |
211
+ | vocabulary | 4,096 custom BPE |
212
+ | optimiser | **Muon** on all 140 hidden matrices, AdamW on embeddings and norms |
213
+ | schedule | warmup β†’ stable β†’ decay (WSD) |
214
+ | corpus | 130,741 conversations Β· 17.8M tokens Β· **56% multi-turn** |
215
+ | training | 2,058 steps from random initialisation |
216
+
217
+ Depth was chosen over width deliberately: an earlier ladder study on this family found
218
+ that narrowing the hidden size cost about 3 points while removing a layer cost ten.
219
+
220
+ ## Files
221
+
222
+ ```
223
+ config.json / model.safetensors the model
224
+ tokenizer.json / tokenizer_config.json custom BPE tokenizer, 4,096 tokens
225
+ loom-tapestry-2-f16.gguf 44MB, for Ollama / llama.cpp
226
+ harness.py runnable harness β€” runs lookups, feeds results back
227
+ template / params read automatically by `ollama run hf.co/...`
228
+ Modelfile for building locally
229
+ ATTRIBUTION.md required credits for the training corpora
230
+ ```
231
+
232
+ ## Training data
233
+
234
+ Openly licensed corpora of real human text, plus a persona curriculum written for Loom.
235
+ See `ATTRIBUTION.md` β€” several of these licences require credit.
236
+
237
+ | slice | source |
238
+ |---|---|
239
+ | grounded reading, and "the result doesn't say" | **SQuAD 2.0** (CC BY-SA 4.0) |
240
+ | when to reach for a tool | **MASSIVE** (CC BY 4.0) Β· **CLINC150** (CC BY 3.0) |
241
+ | instruction following | **databricks-dolly-15k** (CC BY-SA 3.0) |
242
+ | multi-turn dialogue structure | **OpenAssistant OASST1** (Apache 2.0) |
243
+ | identity, limits, warmth, attribution | Textile Labs β€” written for Loom |
244
+
245
+ ## License
246
+
247
+ Model: MIT. Training data retains its original licences and attribution.
params ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ {
2
+ "stop": ["<|eot|>", "<user>", "<result>"],
3
+ "temperature": 0.7,
4
+ "top_k": 40,
5
+ "repeat_penalty": 1.0,
6
+ "num_predict": 96
7
+ }