Providers¶
A provider answers typed questions. ThinkLess ships five, and writing your own takes one class.
Rules¶
Deterministic Python. The cheapest and most predictable provider, and the one that should come first in every cascade.
from thinkless.providers import Rules
rules = Rules()
rules.match("wants_human", r"\b(real person|a human|operator|supervisor)\b", True, field="message")
rules.extract("order", field="message", order_id=r"#\s?(\d{4,5})\b")
@rules.rule("intent")
def empty_message(state):
return "other" if not state["message"].strip() else None
A rule returns an answer to settle the question with confidence 1, or None
to pass it on. Rules only claim questions they have been registered for. A
rule that returns something invalid for the question (an unknown label, a
non-bool for a yes/no) raises, which the engine records as a failed attempt.
GLiNER¶
GLiNER 2.5 by Fastino is a family of schema-driven encoders for classification and extraction, Apache 2.0.
from thinkless.providers import GLiNER
gliner = GLiNER("fastino/gliner2.5-base-v1", device="auto")
| Install | pip install "thinkless[gliner]" |
| Answers | Choice, Extract |
| Checkpoints | gliner2.5-small-v1 (74M), gliner2.5-base-v1 (0.2B, default), gliner2.5-multi-v1 (0.3B, multilingual) |
| Measured latency | 16 ms per classification and 38 ms per extraction on an RTX 5060 laptop GPU; about 85 ms each on CPU |
GLiNER scores every option independently; ThinkLess normalizes the scores into a distribution. All choice questions of one call share a forward pass, as do all extract questions. On Banking77 (77 intents) it was the most accurate provider in our benchmarks, ahead of a 1.7B LLM.
Laya¶
Laya by Convai Innovations is an open-weight, non-autoregressive System One model, Apache 2.0.
from thinkless.providers import Laya
laya = Laya("convaiinnovations/laya", device="auto")
| Install | pip install "thinkless[laya]" |
| Answers | Choice, Score, YesNo |
| Checkpoints | convaiinnovations/laya (English, 421M); see the Laya project for multilingual and fine-tuned checkpoints |
| Measured latency | 30 ms for three questions in one pass on an RTX 5060 laptop GPU; about 650 ms on CPU |
Laya answers every question of a call in one forward pass. It is the only
bundled local model for Score and YesNo. It is weaker on choices with many
options: 35 percent on Banking77's 77 intents, which matches the Laya
project's own report.
SystemOne: Jev, Kev, OpenJev¶
Any server that implements TypeSafe's POST /v1/systemone.
from thinkless.providers import SystemOne
jev = SystemOne.jev() # reads TYPESAFE_API_KEY
kev = SystemOne.self_hosted("http://localhost:8009", name="kev", model="kev-latest")
| Install | nothing extra, it uses httpx |
| Answers | Choice, Score, YesNo |
| Cost | Jev is priced at $0.042 per million input tokens with free output (TypeSafe, September 2026); self-hosted servers are recorded as free |
| Retries | 429, 529, 5xx and connection errors, with exponential backoff that honors Retry-After |
TypeSafe reports 70 to 500 ms per call for Jev. Kev and OpenJev need a large GPU to self-host (Kev-4B wants 32 GB).
HFClassifier¶
Any Hugging Face text-classification model, answering the questions it was
trained for. A general decision model answers anything reasonably; a
classifier trained for one narrow question usually answers that question much
better.
from thinkless.providers import HFClassifier
injection_guard = HFClassifier(
"protectai/deberta-v3-base-prompt-injection-v2",
answers={"injection": {"yes": "INJECTION"}},
field="message",
)
sentiment = HFClassifier(
"cardiffnlp/twitter-roberta-base-sentiment-latest",
answers={"tone": {"upset": "negative", "happy": "positive", "unclear": "neutral"}},
)
| Install | pip install "thinkless[local-llm]" (transformers and torch) |
| Answers | Choice and YesNo, only for the question names in answers |
| Mapping | yes/no: the label that means yes; choice: option to model label |
Put it in the cascade before the general models so it answers its questions first; it never claims any other question. Calibrate it on your own traffic before relying on it: the injection model above is a good example of why. It is widely used, and on customer support tickets it made confident mistakes (see the production guide).
LLMDecider¶
Any LLM answering questions through a prompt, the way most agents make
decisions today.
from thinkless.llm import AnthropicLLM
from thinkless.providers import LLMDecider
decider = LLMDecider(AnthropicLLM("claude-haiku-4-5"))
It answers every kind, including Extract, in one prompt per batch, and
parses the reply leniently (code fences, trailing commas, label case and
spacing). Invalid answers become abstentions. Its answers carry no
probabilities, so the engine treats them as uncalibrated: accepted as the last
word of a cascade by default.
Writing a provider¶
Subclass DecisionProvider, declare what you answer, and return a
ProviderResult:
from typing import ClassVar
from thinkless import Answer, Kind, Plane
from thinkless.providers import DecisionProvider, ProviderResult, render_state
class SentimentProvider(DecisionProvider):
name = "sentiment"
plane: ClassVar[Plane] = Plane.MODEL
kinds: ClassVar[frozenset[Kind]] = frozenset({Kind.CHOICE})
def __init__(self, pipeline):
self.pipeline = pipeline # for example a transformers text-classification pipeline
def supports(self, question):
return super().supports(question) and question.key == "sentiment"
def answer(self, state, questions):
scores = {r["label"].lower(): r["score"] for r in self.pipeline(render_state(state), top_k=None)}
best = max(scores, key=scores.get)
return ProviderResult(answers={key: Answer(value=best, probabilities=scores) for key in questions})
Return probabilities whenever you have them: that is what lets the engine
threshold your answers consistently with every other provider. Return None
for a question you cannot answer. Set calibrated = False on the class if your
answers carry no meaningful confidence. Put slow setup in warmup() so the
first request does not pay for it.
Any fine-tuned Hugging Face classifier can become a provider this way, which is often the best option for a narrow, high-volume question such as prompt injection or toxicity.