A reader the checkpoint ships no port for is something you attach yourself, locally, with vegaml.attach. The engine is never retrained, the reader is never fine-tuned, and none of the original training data is needed, because the alignment is self-supervised: the checkpoint's own root reader generates the shared-space target for any text you bring.
import vegaml
texts = [t for t in open("my_corpus.txt") if t.strip()]
report = vegaml.attach(
"HuggingFaceTB/SmolLM2-1.7B-Instruct", # any instruction-tuned causal LM
texts=texts,
out_dir="vega-u-smollm2-1.7b",
)
print(report["ridge_cosines"]) # how faithfully the port landed
v = vegaml.load("vega-u-smollm2-1.7b") # same engine, byte for byte, new eyesThe text file#
No labels, no questions, no structure. The one thing that matters is that the lines look like your production traffic, because the port tunes to the corpus it is fitted on.
| rule | what it means |
|---|---|
| one document per line | a line is one text: an email, a ticket, a review, a log, a contract, a chat transcript |
| plain text only | UTF-8, no labels, no headers, no JSON, no markup; attach never reads answers here |
| long documents stay on one line | replace newlines inside a document with spaces; blank lines are skipped |
| any length | a sentence to tens of thousands of tokens; include genuinely long lines if you will read long records |
| your domain | whatever the model will actually read in production, in the languages your readers handle |
| diverse beats big | 1,000 varied lines beat 10,000 near-duplicates; do not repeat one template |
I was charged 320.00 twice for the booking system all week. Please refund the duplicate and confirm by email, or I will have to cancel the account.
Review: disappointed with the router. It looked fine out of the box but failed with an authentication loop on the second day, and support has not replied this morning.
Incident report, the north plant: at 06:40 the sensor on line 2 reported a blank screen. The shift lead paused the belt, cleared the jam, and production resumed at 07:05. No injuries.
Full incident timeline, for the record. <...a several-thousand-token document, still one line...>A synthetic starter corpus ships in the repository as examples/sample_attach_texts.txt (1,000 generated lines). Good for a first run; your own domain text is what a production port should be fitted on.
What runs under that call#
Three steps, both label-free.
- Anchor pass. The 0.8B root reader reads every text under six fixed probe questions, covering all three answer types. Its features are the shared-space coordinates, so this writes the targets. The anchor is then freed before your reader loads.
- Port fit. Your reader reads the same prompts, its features are standardized, and one linear map per tapped layer is fitted in closed form (ridge, with lambda picked on a holdout), then refined for a few hundred steps so the frozen engine decides the same way through the new port as through the anchor.
- A servable directory is written: the unchanged engine, the new port, the extraction statistics, and a config that
load()reads like any shipped reader, carryingattach_ridge_cosinesso the fit quality travels with it.
What it costs#
| what to bring | |
|---|---|
| texts | 1,000+ lines from your domain is good, 100 is the floor, diverse beats big |
| GPU memory | enough for your reader itself in bf16: roughly 2 GB per billion parameters plus headroom. The anchor and the engine are noise next to it. |
| time | dominated by reading the texts through your reader, six probe prompts per text. A 7B over 1,000 short texts is tens of minutes on a 24 GB card. |
| labels | none for the attach itself, some for the calibration that should follow |
A 1B reader attaches on any 8 GB card, a 7B to 9B on 24 GB, a 27B wants 80 GB or more. CPU and Apple silicon work too, slower. max_len= caps prompt length if your texts are long and you want a faster first fit.
Read the cosines before you trust the port
report["ridge_cosines"] is the holdout cosine between where your reader's features landed and where the anchor said they should. Well under about 0.8 usually means too little text or too narrow a corpus. More text raises it. This is the number to check before you measure anything else, because a badly landed port makes every downstream number meaningless.
Then calibrate it#
An attached directory answers immediately, but its probabilities are raw until you calibrate on your own labelled examples, which beat any data we could ship because they match your distribution.
import json
cal = json.load(open("examples/sample_calibrate.json")) # or your own, same shape
vegaml.calibrate("vega-u-smollm2-1.7b", cal["examples"], cal["questions"])Roughly 500 labelled examples per question type to be usable. The format is on the Calibration page.
Note
A port fitted on your corpus is tuned to your domain.
Which is the point, and also the caveat: the published benchmark numbers describe only the ports we ship. Measure your own attached reader on your own labels with vegaml-eval before you believe anything about it.
Loading it again later#
attach stores the full repository id it was given in the config's backbone key, so the directory loads by name with no further arguments:
v = vegaml.load("vega-u-smollm2-1.7b")Directories attached under 0.12.0 and 0.12.1 load correctly with no re-attach. A second attach into the same out_dir works, since 0.12.3, on the read-only engine copy the first run inherited from the HuggingFace cache.