eproperty¶
eproperty
¶
A descriptor for a value the interleaver serves during a trace.
An eproperty turns a plain attribute — model.h[0].output,
tracer.result, a source op's .input — into a handoff point in the run: reading
it parks the worker until the model reaches that location and returns the value there;
writing it swaps the worker's value in. The location is "{obj.path}.{key}" (or
just key when the host has no path, as for tracer.result).
The decorated stub is the preprocess: it takes the raw value the interleaver
served and returns what the user reads, so an identity view is just
def output(self, value): return value. Two more callbacks refine it:
postprocess— runs on a written value before it's swapped in (e.g. repack a loneinputback into the(args, kwargs)the model wants).-
transform— the write-back half ofpreprocess. When preprocess hands back a reshaped/sliced view, the user's in-place edits to it are invisible to the model (which still holds the original). A transform maps the edited view back to the model's layout; it fires once, after the block is done with that read, and the result is spliced in as if swapped.class Heads(Envoy): @eproperty(key="output") def heads(self, value): # preprocess: [B,S,H] -> heads b, s, h = value.shape return value.view(b, s, self.n_heads, h // self.n_heads).transpose(1, 2)
@heads.transform def heads(self, value): # write the edited heads back b, nh, s, hd = value.shape return value.transpose(1, 2).reshape(b, s, nh * hd)with model.trace(prompt): model.attn.heads[:, 5] = 0 # zero head 5; transform swaps it back
Both callbacks work in the shape of the location, which is not always a bare
tensor. key="output" on a tuple-returning module serves — and must be handed
back — the whole tuple. key="input" always serves the raw (args, kwargs)
pair, the same value .inputs gives you, so the example above written against
c_proj's input destructures on the way in and repacks on the way out::
@eproperty(key="input")
def heads(self, value):
(x,), _ = value
...
@heads.transform
def heads(self, value):
...
return ((flat,), {})
One failure mode is worth knowing before you meet it: an eproperty is a
property, and a property getter that raises AttributeError falls through
to __getattr__. A preprocess that raises one — a typo, a wrong unpack — is
swallowed and resurfaces as 'Heads' object (nor its module) has attribute
'heads', blaming the attribute rather than the line inside it.
IEnvoy
¶
Bases: Protocol
Interface for objects that host eproperty descriptors.
An eproperty reads and writes its value through the interleaver at a location derived from the host, so a host must provide:
| ATTRIBUTE | DESCRIPTION |
|---|---|
interleaver |
The
TYPE:
|
path |
Optional location prefix used to build the eproperty's location
(
TYPE:
|
Notes
Hosts with no meaningful path (e.g. tracers) need not declare path at
all — eproperty._location uses getattr(obj, "path", ""), so a
missing attribute is treated the same as None / "". It is declared
Optional[str] here only for type clarity.
eproperty
¶
Bases: property
A served value on an interleaving host (Envoy, SourceEnvoy, tracer, ...).
Define one by decorating a stub with @eproperty (or @eproperty(key=...));
the stub is the preprocess. The host only needs a path attribute (and
an interleaver for provide); the value is read/written through
Mediator, which raises outside a trace.
| PARAMETER | DESCRIPTION |
|---|---|
key
|
The location suffix appended to the host's
TYPE:
|
description
|
A short label; only used to surface the attribute in a repr.
TYPE:
|
__call__
¶
Register the decorated stub as the preprocess and adopt its name/doc.
postprocess
¶
Register the write-side callback, run on a value before it's swapped in.
transform
¶
Register the write-back for an edited preprocess view (see class doc).
provide
¶
provide(obj: IEnvoy, value: Any) -> Any
Serve this eproperty's value into the run from the model side.
The counterpart to a worker reading it: hands value to the interleaver at
this location so a worker parked there is resumed with it. Used for values
the model produces outside any module — e.g. a driver feeding
tracer.result.