embedding
Attach your own reader

Docs/Attach your own reader

Attach your own reader

Point vegaml.attach at any instruction-tuned LLM and a file of your own text. No labels, no fine-tuning, no retraining of the engine.


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.

python
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 eyes

The 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.

rulewhat it means
one document per linea line is one text: an email, a ticket, a review, a log, a contract, a chat transcript
plain text onlyUTF-8, no labels, no headers, no JSON, no markup; attach never reads answers here
long documents stay on one linereplace newlines inside a document with spaces; blank lines are skipped
any lengtha sentence to tens of thousands of tokens; include genuinely long lines if you will read long records
your domainwhatever the model will actually read in production, in the languages your readers handle
diverse beats big1,000 varied lines beat 10,000 near-duplicates; do not repeat one template
text
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.

  1. 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.
  2. 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.
  3. A servable directory is written: the unchanged engine, the new port, the extraction statistics, and a config that load() reads like any shipped reader, carrying attach_ridge_cosines so the fit quality travels with it.

What it costs#

what to bring
texts1,000+ lines from your domain is good, 100 is the floor, diverse beats big
GPU memoryenough for your reader itself in bf16: roughly 2 GB per billion parameters plus headroom. The anchor and the engine are noise next to it.
timedominated 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.
labelsnone 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.

python
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:

python
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.