Skip to content

solvi.core.deciders.protocols

The extension points of the parts tier: Scorer, Decider, Adapter, Head (see Building blocks).

The extension points of the parts tier: what a model, a decider, an adapter and a learned answer head implement to plug into solvi. Each is a typing.Protocol (runtime_checkable: isinstance(x, Scorer) checks the methods are there, not their signatures); solvi.testing.conformance checks the behaviour.

from solvi.core import Scorer, Decider, Adapter, Head

Nothing here has to be subclassed: an object with these methods is one. The built-ins conform — OnnxScorer / TorchScorer (Scorer), a decision part and a Cascade / Vote / Route (Decider), the LoRA adapter of solvi.experimental.lora (Adapter), FastHead and the multi-label head of System.fit (Head).

Scorer

Bases: Protocol

A model's raw scores, under DecideModel(scorer).

You implement: logits(items) → one entry per Item (task, options, descriptions, mode, text): an array [K] or [K, C] (a column per mode: [:, 0] choose-one, [:, 1] multi-label) or {"logits": array, "act": logit}. Optional: logits_pass(passes) (several questions in one forward pass), fingerprint() (your weights' identity; without it the model is "unversioned" and a changed model cannot be detected), model_id.

You get for free (through DecideModel): questions from types (choice, multi, score, yes/no, spans), label-bias correction (adapt), few-shot fitting and teach, calibration and act_guard / calibrate_for with a stated promise, the model's identity in every trace and replay that re-runs it.

Stability: stable (no break in 1.x).

Decider

Bases: Protocol

A model as a catalog part: called with the facts it reads, it returns a solvi.Decision(value, probs).

You implement: __call__(**facts) → Decision (the value one of options, or solvi.Unknown when "not stated" is allowed; escalate= a reason when unsure); options → the values it can take (None for a number or a span); fingerprint() → its identity (weights, calibration, adaptation: anything that changes its answers).

You get for free (register it with cat.fn(decider) or return its Decision from @cat.rule): the closed set (an answer outside the options is refused), constraints with joint decoding, guarantees on its signal (System.guarantee), the low-confidence safeguard, its identity recorded with every answer, replay, the audit.

Stability: stable. The built-in decision part and Cascade / Vote / Route are stable to use, provisional to subclass.

Adapter

Bases: Protocol

A per-question adaptation of a model's weights (LoRA and the like), in a decision part's adapter slot.

You implement: kind (a short name; a calibration file names it, solvi.core.deciders.ADAPTERS maps it to the module whose load(part, path, strict, expect=) reads its file); fingerprint() (its content's hash); using(scorer, active=True) → a context in which the scorer scores with it (active=False: with no adapter); save(path) → path; load(path) (a classmethod) → the adapter written there.

You get for free: its fingerprint in the model's and the part's fingerprints (so a decision records which adapter made it, and replay refuses another one), extra["lora"]-style records in the trace, the file written next to a calibration file and loaded back with it (checked against the recorded hash).

Stability: stable (the protocol); the LoRA adapter itself is experimental (solvi.experimental.lora).

Head

Bases: Protocol

A learned answer head: the probability of each option of a question from the facts the System computed.

You implement: options (the answers it scores), features (the facts it reads, set by fit), fit(rows, answers, features) → self (rows: {fact: value} dicts), predict(row) → {option: probability}, contributions(row) → {fact: its share of the answer} (the answer's why), teach(row, answer) → the milliseconds it took (one correction, absorbed at once), and fingerprint() (its parameters: it must change when fit or teach changes the answers).

You get for free: System.fit(question, examples, head=lambda options: YourHead(options)) builds the rows from the flow, selects the facts (select=True) and installs it (a multi-label question gets one per option); the System answers from it — abstaining when a feature could not be computed or the probabilities are not finite — with its fingerprint recorded with every answer (a replay with another head is a mismatch); System.teach updates it; guarantees on its confidence (System.guarantee).

Stability: stable. The built-in FastHead is stable to use, provisional to subclass.