clef on Tenstorrent p150x2
Serves Cloudflare/clef by Cloudflare at revision 2f3de3dd, post-trained from Qwen/Qwen3.8-27B. See the original model card for training and evaluation details.
Clef is a 27B-parameter multimodal decision model from Cloudflare: a Qwen3.8-27B backbone (64 layers, 48 Gated DeltaNet and 16 full-attention, hidden size 5120) with its 27-block vision tower, and a 65M-parameter joint schema head. It reads one state (text, JSON, images or video) and a schema of typed questions (choice, score, noul) and returns a probability for every allowed option of every question in one forward pass. It generates no text. This package serves it through the model's own FastAPI server, which implements the Jev/SystemOne API (POST /v1/systemone), so SystemOne clients work against it unchanged. The weights pointer covers everything the server loads: the backbone shards, the vision encoder, the joint schema head (joint_head.safetensors), the tokenizer and processor configuration, and the author's joint_schema_model.py.
Runs on Tenstorrent p150x2 (mesh P150x2).
Packaged and published with tt-model-manager 0.1.0 (manifest schema 5.1).
At a glance
| Hardware | p150x2 |
| License | apache-2.0 |
Intended use
Typed decisions over a state of up to 16,384 tokens, with or without images (classification, routing, triage, intent detection, extraction choices, policy and eligibility checks, multiple-choice reading comprehension, judging an answer against stated criteria, matching a caption or label to an image), in workflows that act on confidence with thresholds set on a labelled sample of your own data.
Prerequisites
- The Tenstorrent CLI,
tt:uv tool install tenstorrent(or use tt-model alone, see the Quickstart) - Docker
- Tenstorrent hardware: p150x2
Quickstart
tt serve tt-hous/clef
tt serve downloads the Docker image and the Cloudflare/clef at 2f3de3dd85f379784083b0814d997ab627200f0c weights (into your HF cache; they are not in the image), then starts the model's own HTTP server on port 20000 (or the next free port, if that one is busy). The first start compiles kernels for your device, which takes several minutes; the server is ready when it logs Application startup complete.
Without tt-cli: tt-model serve tt-hous/clef.
A browser demo and a command-line client ship in code/models/autoports/cloudflare_clef/demo/ of this repo (inside the image at /opt/tt-metal/models/autoports/cloudflare_clef/demo). With the server up on the port your serve command reported:
hf download tt-hous/clef --include "code/models/autoports/cloudflare_clef/demo/*" --local-dir clef-pkg
cd clef-pkg/code/models/autoports/cloudflare_clef/demo
DEMO_BIND=0.0.0.0 ./run.sh http://<server-host>:<port>
Open http://<demo-host>:8080/#server=http://<server-host>:<port>. The page sends POST /v1/systemone from the browser and draws every option probability: a support-triage preset, the invoice JSON state from the upstream card, a repeated-state preset that shows the prefix cache, two New Yorker cartoons and a synthetic receipt as image requests, and a replay of 24 labelled records (ARC-Challenge, BANKING77, New Yorker) with running accuracy. Against this package every preset answers (8 of 8) and the replay scores 22 of 24.
The CLI needs only Python 3: python3 quickstart.py --all sends every preset, --replay runs the labelled replay, --preset "<title>" sends one, --show-request prints the request bodies without sending them, and --server points it at another host. Presets live in presets.json, shared by the page and the CLI.
Capabilities
This package exposes the model's own HTTP server on port 20000, not an OpenAI-compatible chat API — its request and response shapes are the model's own. See the author's notes for the payload it expects.
Request shape
A request has model, state (any string or JSON value), questions (question id to question), and optional images and videos. A question has type (noul, choice or score), optional instructions, and criteria (a mapping of option id to description for choice, a list of ordered option descriptions for score, optional true and false descriptions for noul). Each image is base64-encoded image bytes (a bare string or a data:image/...;base64, URL) or an http(s) URL; each video is a list of frames in the same encodings. No API key is checked unless the server was started with CLEF_API_KEY.
Text example (the Jev/SystemOne example of the upstream model card), with <port> the port your serve command reported:
curl -s http://127.0.0.1:<port>/v1/systemone -H 'content-type: application/json' -d '{
"model": "clef",
"state": "Our checkout started returning errors and orders are blocked.",
"questions": {
"department": {"type": "choice", "instructions": "Which team should handle the message?",
"criteria": {"billing": "Payments or invoices", "technical": "Bugs or outages"}},
"urgency": {"type": "score", "criteria": ["Can wait", "This week", "Today"]},
"outage": {"type": "noul", "instructions": "Is a service down?"}
}
}'
The same request, and an image request, from Python:
import base64
import requests
URL = "http://127.0.0.1:<port>/v1/systemone"
text = requests.post(URL, timeout=300, json={
"model": "clef",
"state": "Our checkout started returning errors and orders are blocked.",
"questions": {
"department": {"type": "choice", "instructions": "Which team should handle the message?",
"criteria": {"billing": "Payments or invoices", "technical": "Bugs or outages"}},
"urgency": {"type": "score", "criteria": ["Can wait", "This week", "Today"]},
"outage": {"type": "noul", "instructions": "Is a service down?"},
},
}).json()
print(text["answers"]["department"]["choice"], text["answers"]["urgency"]["score"], text["answers"]["outage"]["noul"])
with open("receipt.jpg", "rb") as f:
image_b64 = base64.b64encode(f.read()).decode()
image = requests.post(URL, timeout=300, json={
"model": "clef",
"state": {"task": "Review the attached receipt."},
"images": [image_b64],
"questions": {"legible": {"type": "noul", "instructions": "Is the receipt total legible?"}},
}).json()
print(image["answers"]["legible"]["noul"], image["usage"]["input_tokens"], image["latency_ms"])
Response shape
The response body is the one the author's joint_schema_model.systemone() returns (model, answers, usage) plus latency_ms. Answer shapes follow the upstream systemone(): choice has choice, confidence and probabilities; noul has noul (the probability of true); score has score, confidence, legend and probabilities. For the text example above this package answers department = technical (probability 0.9138), urgency score 1.83 (Today at 0.8706) and outage noul 0.9011, with 300 input tokens and about 270 ms of model time; the author's code on CPU (bf16) answers technical (0.9155), 1.82 (Today at 0.8651) and 0.8995 with the same 300 input tokens.
Routes
| Route | Body |
|---|---|
POST /v1/systemone |
{"model", "answers", "usage": {"input_tokens", "output_tokens"}, "latency_ms"} |
GET /v1/models |
One entry per served name (clef) with the weights revision, device, engine mode, the limits and the prefix-cache counters. |
GET /health, GET /v1/health |
{"status": "ok", "workers", "queued"}. Never behind the API key. |
A state over CLEF_MAX_STATE tokens, or a schema over CLEF_MAX_TAIL tokens, returns 422 with the token count. A request whose questions have no criteria (other than noul) returns 422, as the upstream systemone() raises. In the traced opt-in mode (below) an image grid outside the warm list, or a request with more than one image or video, returns 422.
Server environment
The defaults below are the shipped configuration (eager engine, prefix planner on), the one every number under Expected performance was measured with. tt-model serve has no flag for extra environment variables; to change one, run the image with docker run --env.
| Variable | Default | Meaning |
|---|---|---|
HF_MODEL |
Cloudflare/clef (set by tt-model) |
Hub id or snapshot directory of the weights. |
CLEF_REVISION |
2f3de3dd85f379784083b0814d997ab627200f0c |
Revision used when the model id has no @rev. |
HF_HUB_OFFLINE |
unset | 1 resolves Hub ids from the local cache only. |
CLEF_MAX_STATE |
16384 |
Limit for the state piece (system prompt and media placeholder tokens included); the upstream encode_record limit. Longer states return 422. |
CLEF_MAX_TAIL |
4096 |
Limit for the schema plus the suffix. Longer schemas return 422. |
CLEF_TRUNCATE_STATES |
0 |
1 keeps the first CLEF_MAX_STATE tokens instead of refusing, and marks the response. |
CLEF_PREFIX_CACHE |
4 |
States kept in the prefix cache; 0 disables the cache. |
CLEF_PLANNER |
1 |
The prefix planner picks the cached prefix length so a cache miss takes the fewest prefill passes; 0 restores the fixed 128-aligned rule. |
CLEF_TRACED |
0 |
0 is the shipped eager engine: any image grid, any number of images or videos per request. 1 is the traced opt-in for a fixed set of image grids: it needs CLEF_VISION_WARM_GRID, refuses an image or video whose grid is not in that list and a request with more than one image or video (422), and reserves CLEF_TRACE_REGION of DRAM. It gains 0 to 3 percent of latency. |
CLEF_VISION_WARM_GRID |
unset (required when CLEF_TRACED=1) |
;-separated t,h,w patch grids the traced server warms and then accepts, for example 1,16,20;1,22,38;1,26,36;1,28,36;1,28,38;1,40,50;2,16,20. Ignored in eager mode. |
CLEF_TRACE_REGION |
1073741824 when CLEF_TRACED=1, else 0 |
Trace region size for the device. |
CLEF_API_KEY |
unset | Bearer key for /v1/* (health routes stay open). |
CLEF_ALLOW_REMOTE_IMAGES |
1 |
0 refuses http(s) image URLs with 422. Fetches use a 10 s timeout and a 20 MiB cap. |
CLEF_MODEL_NAMES |
clef |
Comma-separated names listed by /v1/models and accepted in model. |
CLEF_WARMUP |
1 |
0 skips the warmup request per worker at startup. |
Expected performance
Evaluations
| eval (test split) | this package | reference (Cloudflare/clef card) | items |
|---|---|---|---|
| ARC-Challenge, accuracy | 97.8 | 97.7 | 1,172 |
| BANKING77, macro-F1 over 77 intents | 94.4 | 94.2 | 3,080 |
| New Yorker caption matching (image, 5 captions), accuracy | 58.3 | 69.5 | 528 |
Public benchmarks rendered once to SystemOne requests (no prompt tuning, no option dropping) and run against this server on profile p150x2; coverage is complete on every file (0 rejected, 0 truncated, 0 unanswered). The reference figures are the Decision Index results on the Cloudflare/clef model card, whose request renderings are not public.
The same rendering on a stratified 100-item sample of each benchmark, this package against the author's own joint_schema_model.py on CPU in bf16 (same metric as above):
| 100-item sample | this package | CPU bf16 |
|---|---|---|
| ARC-Challenge | 96.0 | 95.0 |
| BANKING77 (macro-F1) | 91.1 | 91.1 |
| New Yorker | 60.0 | 60.0 |
On ARC one argmax differs between the two (reference top-2 margin 0.37); on BANKING77 none (2 near ties).
Kev's labelled suites (hard-v1, devtools-v1 and documents-v1 test splits, SystemOne requests, kev.benchmark --remote, clean block): accuracy, Brier, and ECE (expected calibration error, 10 bins on the top probability); coverage complete. The Cloudflare/clef card publishes no figure for these suites.
| suite (test split) | questions | accuracy | Brier | ECE | reference |
|---|---|---|---|---|---|
| hard-v1 | 1,088 | 0.774 | 0.321 | 0.044 | — |
| devtools-v1 | 1,073 | 0.741 | 0.399 | 0.143 | — |
| documents-v1 | 936 | 0.893 | 0.169 | 0.044 | — |
The same requests against tt-hous/kev-9b on one p150 score 0.826 / 0.787 / 0.896 accuracy (ECE 0.053 / 0.101 / 0.015); these suites are Kev-9B's own development distribution.
Latency
Profile p150x2 (the only profile). Latency is the server's latency_ms: the model section of the request on its worker thread (state prefill or cache restore, schema continuation, vision tower when images are present, joint schema head on the host), without queue wait and HTTP. "New state" is a prefix-cache miss; "repeated state" is a hit (the KV pages and the Gated DeltaNet state of the slot are restored, then the schema tail runs). A state under 128 tokens has no cached prefix, so the two are equal by construction for short states.
| request | state | N | median, new state (ms) | median, repeated state (ms) | p95 (ms) |
|---|---|---|---|---|---|
| 6 questions | short (122 tokens with the system prompt) | 20 | 412.6 | 410.4 | — |
| 5 questions | 2,200 tokens | 20 | 1,231.2 | 533.8 | — |
| 3 questions | 346-token blog triage state | 20 | 268.2 | 265.0 | — |
| 2 questions | short | 20 | 267.9 | 257.8 | — |
| 5 questions | 370 tokens | 20 | 426.0 | 423.1 | — |
| 1 image (320 patches) and 5 candidate captions | 330 tokens | — | 497.7 | 237.1 | — |
A request costs about 220 ms of fixed work plus about 0.35 s per 1,024-token chunk of prefill; a 16,384-token state (the limit) takes 7,471.8 ms on a miss and 1,600.0 ms on a hit. Over the 8 reference image records (320 to 2,000 patches) the median is 551 ms. The largest image run end to end is 57,344 patches ((1, 224, 256), 3584 x 4096 pixels, 14,336 image tokens): 33.2 s per request warm; a 2048 x 2048 image (16,384 patches, 4,096 tokens) takes 5.7 s. Above 4,096 patches the vision tower time grows about quadratically.
Throughput: 2.36 requests/s at 64 concurrent clients for the 6-question short-state case (256 requests over 64 distinct states per client level at 1, 8, 32 and 64 clients, two passes, the second timed); it is flat in the client count (2.35 to 2.36 requests/s) because requests are served one at a time, and the queue wait grows linearly with the clients. 64 distinct new 2,200-token states give 0.81 requests/s.
Measured on 2026 Oct 05 on two Blackhole chips of one p300 board (profile p150x2, the shipped configuration: eager engine, prefix planner on) with the port's scripts/serving_bench_remote.py over HTTP; latency rows are medians of 20 requests after 2 warm-up requests. Precision: bfp8 weights for the MLP gate and up projections and for the attention and Gated DeltaNet projections, bf16 MLP down weights, bf16 KV cache and embeddings, fp32 Gated DeltaNet recurrent state and decay gate, vision tower in bf16 with fp32 accumulation, joint schema head in fp32 on the host.
Parity against the author's CPU reference
Over HTTP against the served package. Max dp is the largest absolute difference of an option probability; flips are argmax changes at a reference top-2 margin of at least 0.05.
| set | questions | max dp | flips at margin | reference |
|---|---|---|---|---|
| 16 reference text records | 27 | 0.072 (mean 0.011) | 0 | CPU bf16 |
| same 16 records | 27 | 0.076 (mean 0.010) | 0 | CPU fp32 |
| 8 reference image records | 8 | 0.046 (mean 0.020) | 0 | CPU bf16 (0.038 against fp32) |
| 100 BANKING77 rows (1,824 to 1,871 tokens) | 100 | 0.110 | 0 (2 near ties) | CPU bf16 |
| 8 long records, 2,637 to 16,384 tokens | 32 | 0.056 | 0 | CPU bf16 |
| 64 development text records | 95 | 0.192 | 1 (margin 0.17, dp 0.10) | CPU bf16 |
| 16 development image records | 16 | 0.175 | 0 | CPU bf16 |
Vision tower parity against the HF model.visual on CPU on the 4 reference images: merged-output PCC 0.99880, 0.99822, 0.99909 and 0.99898; the 4-frame video record 0.99839.
Limitations
- Tested on two Blackhole chips of one p300 board (profile
p150x2, the only profile) only. English only: the benchmarks above are English and other languages were not evaluated. The Decision Index of the upstream card was not reproduced; its request data is not public. - Not for text generation, chat, summarisation or open-ended question answering: the model only scores the options it is given. Not for fully automated decisions with legal, medical, financial, employment or similar consequences for people without human review. The SystemOne
permuteandseparateroutes are not served. - Requests are served one at a time: 2.36 requests/s for short states and 0.81 for new 2,200-token states at any client count, and the queue wait grows linearly with the number of clients (27 s at 64 clients).
- Startup takes 106 s on every start (mesh open 4 s, weights converted from the safetensors and the model built in 100.6 s, one warmup request); no tensor cache is written.
- A state over
CLEF_MAX_STATEtokens (16,384, the upstreamencode_recordlimit) or a schema overCLEF_MAX_TAIL(4,096) returns 422 instead of the upstream silent truncation (CLEF_TRUNCATE_STATES=1opts into truncation). The 262,144-token context the HF config advertises is not served. - A few confident decisions per thousand move by up to 0.3 in probability against the author's CPU reference: on a 200-record development set 274 of 278 argmaxes agree and accuracy is within 0.36 pp, but two questions move by 0.33 and 0.47 while the reference is confident; over all parity sets there is one argmax flip at a reference margin of at least 0.05 (margin 0.17). One development image record moves by 0.175 with the argmax kept.
- Images run through the on-device vision tower in bf16. The largest image run end to end is 57,344 patches (3584 x 4096 pixels, 14,336 image tokens, 33 s per request); above 4,096 patches the vision tower time grows about quadratically. The first image of a patch grid the box has not seen compiles once (1 to 2 s at up to 2,048 padded rows, 6 to 9 s at 4,096, 26 s at 57,344); the kernel cache under
~/.cache/tt-model/clef/cachepersists across container restarts. Images and videos are not accepted together in one request (one media kind per request). - Traced mode (
CLEF_TRACED=1) needsCLEF_VISION_WARM_GRID, refuses image grids outside that list and more than one image or video per request (422), reserves a 1 GiB trace region, and gains 0 to 3 percent. - The prefix cache holds
CLEF_PREFIX_CACHEstates (4 at the 16,384-token limit); only the 128-aligned state prefix is cached, the schema always runs, and a state under 128 tokens is never cached. The joint schema head runs on the host CPU over every row of the record, so the host part grows with the state length (a 16,384-token cache hit takes 1,600 ms). - Option order can change an answer. Knowledge is set by the base model.
- Calibrated probabilities can create unwarranted trust: measure accuracy and calibration on a labelled sample of your own data before setting thresholds, and monitor error rates in production.
- The server is open unless
CLEF_API_KEYis set and fetches http(s) image URLs unlessCLEF_ALLOW_REMOTE_IMAGES=0; states, images and videos may contain personal or confidential data, so put the server behind your own access control and do not expose it to untrusted networks without a proxy.
Licensing
Cloudflare/clef (backbone, vision encoder, joint schema head and joint_schema_model.py) is released under Apache-2.0; its model card states the base model Qwen/Qwen3.8-27B is Apache-2.0. The serving code under code/ is tt-metal (Apache-2.0) plus the Clef port under models/autoports/cloudflare_clef (Apache-2.0, see its NOTICE), which vendors the joint schema head classes from joint_schema_model.py and reuses serving code from the Apache-2.0 kev project. The browser demo (in code/models/autoports/cloudflare_clef/demo, also inside the image) ships ten New Yorker caption contest cartoons from the jmhessel/newyorker_caption_contest dataset under CC BY 4.0, as its NOTICE states.
Related packages
Upstream model card: https://huggingface.co/Cloudflare/clef. Announcement: https://blog.cloudflare.com/clef-decision-models. Decision Index leaderboard: https://clef-evals.workers-ai-mle.workers.dev. The smaller variant Cloudflare/clef-flash is not packaged. The same serving pattern on one Blackhole chip: tt-hous/kev-9b.
Feedback
Questions or problems with this package: open a discussion at https://huggingface.co/tt-hous/clef/discussions — that is what reaches its author. A problem with the tt tooling itself: tt report issue (collects your environment and opens a prefilled issue against tenstorrent/tt-cli). Product feedback: support@tenstorrent.com.
Provenance
The exact sources the image was built from — code/ in this repo is byte-identical to the model code inside the image:
| component | built from |
|---|---|
| tt-metal | c72c144e6e4d4cb1a29ee827cfb98d66b9883c30 (dirty tree — the image includes uncommitted changes) |
code/ digest |
f2f49abe472ac6d3 (sha256, first 16 hex digits) |
| built | 2026-10-06T14:13:56+00:00 by tt-model 0.1.0 |