Expand description
cratefield-adapter-classifier-llm: the Classifier port over the
TextModel port the venture already wired (issue #456) — the third of
the three Classifier adapters, and the only one with no vendor of its
own. It reaches whichever model the TextModel tiers are routed to, a
local model included, through JSON-schema structured output, and it is
the honest fallback when neither a purpose-trained classifier nor a
vendor binding is wired.
Calibration is the language model’s. This adapter reports
Calibration::LanguageModel, and the point is not nominal: a
probability elicited from a general language model is not the same
number as one from a purpose-trained classifier. A 0.8 here is the
model’s own rating of its certainty — a different sharpness at the
same number, drifting with every model version the venture re-wires
the tier to — so a threshold tuned against a Calibration::Classifier
adapter is wrong against this one, and a module that thresholds on
Answer::confidence says in its own docs which calibration it tuned.
The state ceiling is inherited, and the port cannot see the real
one. Whatever TextModel allows, this adapter allows: it reports
the port’s conservative DEFAULT_MAX_STATE_CHARS by default, and
.max_state_chars exists because the actual budget is the underlying
model’s context window, which the TextModel port has no way to
report. Whoever wires the model knows it and says so here.
Crate docs use plain backticks, never intra-doc links — the same rule the rest of the classifier work follows.
§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 cratefield_adapter_classifier_llm::ClassifierLlm;
use std::sync::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(Arc::new(ClassifierLlm::new(text_model)));
// 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 = ClassifierLlm::new(text_model)
.tier(cratefield_core::ModelTier::Fast)
.max_state_chars(24_000);§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.
Structs§
- Classifier
Llm Classifierover the venture’s ownTextModelwiring. Construct withClassifierLlm::newand the two builder methods; everything else is theClassifiertrait.