NORTHTEK Labs · Research

SOFAR: An Adapter I Built So Transformers Stop Forgetting

An open-source adapter for HuggingFace transformer models. It finds the directions inside a model that carry signal across many layers, and nudges attention through them, without touching the model's own weights.

2026 NORTHTEK Labs github.com/NORTHTEKDevs/SOFAR

What this is

Here's the thing about language models: they technically "see" your whole input. Feed one a 100-page contract, a 50-file codebase, a 90-minute conversation transcript, and yes, all of it is sitting there in the context window. But by the time the model gets to the end of that input, it's reliably lost track of what was at the beginning. That's the long-context degradation problem, and it shows up in pretty much any LLM application that runs long enough.

The usual fix is RAG, retrieval-augmented generation: build a search index, look up the relevant snippets, paste them back into the prompt. RAG works, but it doesn't actually fix the problem, it works around it. You're bolting a second system onto a model that still can't reliably use the context you already gave it.

I built SOFAR (Signal-Optimized Frequency-Aligned Routing) to try a different angle. It attaches to an existing HuggingFace transformer as a small adapter, it doesn't touch the host model's weights at all, and it nudges attention toward the internal directions that hold onto information across layers instead of losing it. It's not trying to replace RAG for information that lives outside the prompt. It's aimed at something narrower: making sure the model can actually use the context it was already handed.

The numbers

These come straight from the project's published benchmark recipe, run against plain HuggingFace baselines on a single GPU. The recipe itself, analyze the model, train the adapter, run the needle-in-a-haystack retrieval benchmark (hide a fact somewhere in a pile of text, see if the model can still find it), is identical no matter what size model you point it at. The README notes that the lift scales up with model size.

Model Context Baseline needle SOFAR-trained needle Lift
GPT-2 small (124M) 256 0.0% 33.3% +33%
GPT-2 small (124M) 512 0.0% 27.8% +28%
GPT-2 small (124M) 768 0.0% 44.4% +44%
GPT-2 medium (355M) 256 0.0% 100.0% +100%

The CLI that produced this table ships with the project, so you can reproduce it yourself or point it at a different model:

sofar map gpt2                                           # analyze the model
sofar train gpt2 gpt2_channels.json --epochs 3           # train the adapter
sofar bench gpt2 --patched --channel-map gpt2_channels.json
sofar report results/                                    # markdown comparison

Behind those numbers, the codebase has 365 unit and integration tests at 100% line coverage, running under strict-warning CI (pytest -W error), including dedicated pickle, deepcopy, and multiprocessing round-trip tests. I care about this part almost as much as the benchmark table. A research result nobody can trust to run twice isn't worth much.

SOFAR attaches as an adapter and never touches the host model's weights, it's about 0.03% the size of the model it's attached to. The same code path handles GPT-2 (which stores its weights in a different orientation, Conv1D instead of the usual Linear layer), Llama, Mistral, and Qwen (which split attention into separate Q/K/V/O projections), plus quantized versions of those models (bitsandbytes 8-bit and 4-bit, GPTQ, AWQ). Apache-2.0 licensed. Repo's at github.com/NORTHTEKDevs/SOFAR.

Why this might matter

I hold two versions of this in my head at once, and I think both are fair.

The careful version: SOFAR shows that a lightweight, weights-only look at a model's internal structure can find which parts of its attention are worth reinforcing, and that a small trained adapter built along those lines produces a measurable needle-retrieval lift on GPT-2 small and medium, across three different context lengths. The adapter is tiny, it never touches the host weights, and the benchmark recipe is public, anyone can re-run it and check my numbers.

The version I'd love to be true: the README states the lift scales with model size, and the same approach already runs across GPT-2, Llama, Mistral, Qwen, and quantized model formats through one code path. If that holds up as models and context windows keep growing, it's a cheaper complement to reaching for RAG every time a model loses the thread of its own input. I built SOFAR to work alongside persistent memory systems, not compete with them: the memory layer pulls the right window of context, and SOFAR helps the model actually use the whole thing once it's pulled.

Honestly, the real answer sits somewhere between those two: a genuine, reproducible result on two GPT-2 sizes at context lengths up to 768 tokens, and an open question about whether it holds at the model sizes and context lengths people actually run in production. I don't know yet. That's part of why it's out in the open.

What it isn't

Trust matters more to me than a good demo, so here's what SOFAR does not do, straight from my own disclosures in the project:

How it actually works, in plain English

Whales talk to each other across thousands of miles of open ocean. Not by calling louder. There's a horizontal layer in the ocean called the SOFAR channel where sound travels enormous distances with almost no loss, and whales drop their calls into it and let the water carry the message. That's where the name comes from.

I went looking for the equivalent inside a transformer: directions in the model's internal math where information can travel across many layers without degrading, the way sound travels through that ocean channel. Find those directions, route attention through them, and the model holds onto more of what you gave it.

SOFAR runs as a small adapter attached to the model, and it never modifies the model's own weights. It first looks at the model's own internal structure to find the directions that reliably carry information across layers, then trains a lightweight adapter, about 0.03% of the model's size, that steers attention along those directions. It's built to hold up across different long-context retrieval shapes, not just the needle-in-a-haystack setup used for the benchmark table above. If you want the full technical detail, it's all in the open-source repo, I didn't hide anything behind this writeup.

It hooks into the model non-invasively, so the model's own forward pass, the actual computation it runs on every input, is never touched or rewritten. The adapters are picklable (you can save and load them like any Python object), multiprocessing-safe, aware of DDP / FSDP / DataParallel wrapping (the common ways people spread a model across multiple GPUs; SOFAR unwraps them automatically), and quantization-aware, meaning it works whether the model has been shrunk down with bitsandbytes 8-bit and 4-bit, GPTQ, or AWQ. In code, attaching it looks like this:

from transformers import AutoModelForCausalLM
from sofar import map_channels, patch

model = AutoModelForCausalLM.from_pretrained("gpt2")
channel_map = map_channels(model)        # analyze the model, offline
patched = patch(model, channel_map)      # attach the adapter

Where to go deeper

If you want more than the plain-English version, here's where to look:

Go read the code

The full repo is on GitHub, Apache-2.0 licensed. Use it, change it, ship it inside a proprietary product if you want to; the only thing I ask is that you keep the license and attribution notices in whatever you build on top of it.

github.com/NORTHTEKDevs/SOFAR →

Built by Kristian Baer / Northtek in Anchorage, Alaska. Research and portfolio code, Apache-2.0 licensed. If you use this in your own work, there's a citation in the repo README.