clm
2 TagsUpdated Apache-2.0by Contrastive-LM
Contrastive decision model by Contrastive-LM: the Qwen3-8B encoder embeds the state and every option, and two trained heads pick the option closest to the state. Repeated questions and options are cached.
ollaya run clm --preset triage "I was charged twice for my subscription this month and want a refund."curl http://localhost:11435/api/decide \
-H "Content-Type: application/json" \
-d '{
"model": "clm",
"state": "I was charged twice for my subscription this month and want a refund.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoices and refunds",
"technical": "Bugs, errors and outages",
"account": "Login, profile and settings"
}
},
"refund": {
"type": "noul",
"instructions": "Is the customer asking for a refund?"
}
}
}'# Already using a TypeSafe SDK? Set TYPESAFE_BASE_URL=http://localhost:11435 instead.
import requests
response = requests.post(
"http://localhost:11435/api/decide",
json={
"model": "clm",
"state": "I was charged twice for my subscription this month and want a refund.",
"questions": {
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "Payments, invoices and refunds",
"technical": "Bugs, errors and outages",
"account": "Login, profile and settings"
}
},
"refund": {
"type": "noul",
"instructions": "Is the customer asking for a refund?"
}
}
},
)
answers = response.json()["answers"]
print(answers["department"]["choice"], answers["refund"]["noul"])const response = await fetch("http://localhost:11435/api/decide", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "clm",
state: "I was charged twice for my subscription this month and want a refund.",
questions: {
department: {
type: "choice",
instructions: "Which team should handle this?",
criteria: {
billing: "Payments, invoices and refunds",
technical: "Bugs, errors and outages",
account: "Login, profile and settings"
}
},
refund: {
type: "noul",
instructions: "Is the customer asking for a refund?"
}
}
}),
});
const { answers } = await response.json();
console.log(answers.department.choice, answers.refund.noul);Models
Every tag| Name | Accuracy | Latency | |||
|---|---|---|---|---|---|
| Most accurateHighest typed-decisions accuracy of the family. | |||||
| clmlatestSame as clm:8b. | 0.357 | 95 ms | |||
| FasterFastest first; accuracy as measured on the bench. | |||||
| clm:8bCLM-v0.1-8B on Qwen3-8B, 0.357 on typed decisions; built for agent, game and tool-calling states. Needs a 20 GB GPU. | 0.357 | 95 ms | |||
Accuracy: typed-decisions, argmax against the majority label on 400 states (left out for a tag trained on that data). Latency: the median five-question request on an RTX 5090, Ollaya Bench.
Readme
CLM is a contrastive decision model by Contrastive-LM, released under Apache-2.0. Instead of reading answer logits, it embeds the state and every option with the Qwen3-8B encoder, projects them with two small trained heads (one for states, one for options) and picks the option most similar to the state. Options and questions are embedded on their own, so Ollaya caches them: a question you ask again costs only its state.
Needs Ollaya 0.7.4 or newer, which runs the
clm-v1layout. Update first (ollaya --version); an older version downloads the weights and then refuses to load them.
Models
| Tag | Encoder | Params | Typed-decisions accuracy |
|---|---|---|---|
clm:latest, clm:8b | Qwen3-8B (last-token embedding) | 8.2B | 0.357 |
Typed-decisions accuracy is the argmax against the majority label on all 400 typed-decisions states. On these structured workflow states CLM is close to chance and leans towards "yes"; its authors report its strength on computer-use, gaming and tool-calling states, and as a fine-tuned verifier. For general triage, winnow:e4b (0.724) is the better pick.
Usage
ollaya run clm --preset triage "Third time this year you've double-charged me. Refund it today or I'm cancelling and moving to a competitor."Point any TypeSafe client at http://localhost:11435 and set the model to clm. Give choice criteria as an object of option to description ({"refund": "wants money back", ...}); CLM embeds each description, so descriptions matter more than labels.
Speed
- RTX 5090, through the HTTP API (Ollaya Bench): five questions on a new short message take 95 ms at the median once the questions are cached, under a millisecond for a repeated request and about 0.5 s with nothing cached.
- Memory: the encoder's weights stay BF16, about 16 GB; use a GPU with 20 GB or more. On the Threadripper 3960X (24 cores) a request takes 5230 ms.
How it works
- Texts. The state is rendered as prose (JSON objects as
key: valuefields), followed by the question's instructions. Each option is its description; a noul question compares "Yes. This is true: …" with "No. This is false: …". - Scores. An option's logit is 100 × the cosine between its projection and the state's; the answer is the softmax over the options. There is no other calibration.
- Weights. The Qwen3-8B weights download from Qwen's repository and the heads from Contrastive-LM's
CLM_v0.1-8B.pt, both pinned to a commit and verified by sha256. Ollaya hosts only the ONNX graph (7 MB). - Parity. Ollaya's Rust runtime matches the reference exactly on texts and tokens, and on the decision of every test question, on CPU and CUDA (probabilities within 3 × 10⁻⁵).
Limits
- Texts over 2,048 tokens are rejected (422); upstream would cut them.
- Choice criteria must be an object, as upstream CLM requires.
Badge
For the model card or a README. Day-0 support for a new model
[](https://ollaya.dev/library/clm)