cratefield-adapter-classifier-llm
Classifier port over the harness's own TextModel port for the Cratefield
harness (issue #456). The third of the three Classifier adapters and the
only one with no vendor of its own: it reaches whichever model the
venture's TextModel tiers are wired to — a local model included — asking
for JSON-schema structured output. When neither a purpose-trained classifier
nor a vendor binding is wired, this is the honest fallback.
Usage
use ClassifierLlm;
use Arc;
// `text_model` is the `Arc<dyn TextModel>` the venture already wired —
// the same router its drafting modules use. No new account, no new key.
let runtime = runtime.classifier;
// A model whose context window cannot carry the default 96_000-char
// state budget says so here, the only place that knows it:
let adapter = new
.tier
.max_state_chars;
Calibration is the language model's
This adapter reports Calibration::LanguageModel, and that is not a
technicality. A probability elicited from a general language model is not
the same number as one from a purpose-trained classifier. A 0.8 this
adapter returns is the model's own rating of its certainty — sharper when
the model is confident, floppier at the decision boundary, and drifting
with every model version the venture re-wires the tier to. A threshold
tuned against a Calibration::Classifier adapter is simply wrong against
this one. A module that thresholds on Answer::confidence documents the
calibration it tuned against, and re-tunes when the adapter swaps.
Behaviour
- One
TextModel::completecall perask, carrying all questions — asking as a set is the port's whole point. The prompt requests structured output throughPrompt::json_schema, with a schema built per call that pins, for every question id, the chosen label and a probability for every label the question actually offers. - Structured output is a request: a provider that cannot honour the
schema answers plain text (
Completion::jsonisNone). The adapter then parsesCompletion::textas JSON, tolerating the markdown code fence real models wrap it in. Neither yields a JSON object with ananswersmapping and the call failsTransport— with a message that does not quote the completion back. - The model's answer is validated, never trusted: a missing or invented
question id, a chosen label the question never offered, a probability
under a label the question never offered, or a distribution that is not
finite and normalisable is
Rejected. A distribution that sums to something other than 1 is normalised; NaN and infinities never pass. statelonger thanmax_state_charsis truncated with the port's deterministic, char-boundary-safe cut, and atracing::warn!fires. The default is the port's conservative ceiling; the real ceiling is the underlying model's context window, which this port cannot see — set.max_state_charsto match whatever the tiers are wired to.- Errors map one-to-one from
TextModelError:NotConfigured(the tier is unwired — how "no classifier vendor wired" surfaces honestly),Rejected,Transient { retry_after }with the back-off preserved, andTransport. Provider text only ever rides inside variants whoseDisplayscrubs it.
MIT. Built in the open for Cratefield, a Factory Zero venture.