lev answers typed questions about a piece of context in a single forward pass. You give it a state (text, a ticket, an email, or JSON) and a set of yes/no, choice, and score questions. It reads each answer from the logits it already computed and returns calibrated probabilities over exactly the options you supplied. It is a LoRA adapter on Qwen3.5-4B, and it speaks TypeSafe's /v1/systemone protocol, so code written for the TypeSafe SDK works against it once you change the base URL.
98
8 commits
2 linked in READMEs
updated Sep 25, 2026
lev answers typed questions about a piece of context in a single forward pass. You give it a state (text, a ticket, an email, or JSON) and a set of yes/no, choice, and score questions. It reads each answer from the logits it already computed and returns calibrated probabilities over exactly the options you supplied. It is a LoRA adapter on Qwen3.5-4B, and it speaks TypeSafe's /v1/systemone protocol, so code written for the TypeSafe SDK works against it once you change the base URL.
Qwen3.5-4B + LoRA on one H100. On the six subsets the public S1Bench board completed, level with reflex-4b and behind only Jev and three open models of 26B–35B.
Quickstart · Self-hosting · Benchmarks · Speed · Why it works · Training · Boundaries · GitHub
No generated tokens, no JSON to parse, no retries. lev cannot return a label outside your options, because the answer space is the option set you send. It can still pick the wrong option: the guarantee is structural, not a guarantee of correctness.
| Question | You give | You get |
|---|---|---|
noul | a yes/no question | noul = p(yes) |
choice | instructions + options (name → description or null) | choice, probabilities, confidence |
score | instructions + 2–10 ordered levels | score (expected level), probabilities, confidence |
It is built for the high-volume judgement calls inside a product: routing, moderation, intent detection, triage, grading, and checking LLM output.
pip install "lev[serve] @ git+https://github.com/Abhinavexists/lev#subdirectory=packages/lev"
This needs Python 3.12 or newer and, for real-time use, a CUDA GPU. The serve extra installs torch, transformers, peft, and the HTTP server. The first load downloads the base model (Qwen/Qwen3.5-4B, about 8 GB) and this adapter (about 200 MB).
import lev
model = lev.load("interfaze-ai/lev")
state = "Hi, I was charged twice for my order #4471 and I want a refund."
questions = {
"intent": {
"type": "choice",
"instructions": "What does the customer want?",
"criteria": {
"refund": "wants money back",
"cancel": "wants to cancel an order",
"track": "wants to know where an order is",
"other": "anything else",
},
},
"urgent": {"type": "noul", "instructions": "Does this need a human within the hour?"},
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["calm", "mildly annoyed", "annoyed", "angry"],
},
}
result = model.system_one(state, questions)
print(result.answers["intent"].choice) # refund
print(result.answers["intent"].probabilities)
# {'refund': 0.84, 'cancel': 0.094, 'track': 0.012, 'other': 0.054}
print(result.answers["urgent"].noul) # 0.43
print(result.answers["frustration"].score) # 1.57, between "mildly annoyed" and "annoyed"
print(result.usage.output_tokens) # 0
These are real outputs from this checkpoint. The answers are objects, and result.model_dump() gives the same JSON the HTTP server returns.
All the questions share one forward pass, so asking three questions costs about the same as asking one. lev.load reads lev_release.json from this repository to find the base model and the prompt format the adapter was trained with. It also applies the shipped calibration and loads the matching head, so there is nothing to configure.
Because the probabilities are calibrated, you can gate on them. For example, act automatically above 0.9 and send anything lower to a person.
lev serve --checkpoint interfaze-ai/lev --host 0.0.0.0 --port 8000
curl -s localhost:8000/v1/systemone -H 'content-type: application/json' -d '{
"state": "The package arrived crushed and the screen is cracked.",
"questions": {"damaged": {"type": "noul", "instructions": "Was the item damaged?"}}
}'
Existing TypeSafe clients work once you point them at the server:
from typesafe_sdk import Choice, Noul, TypeSafeClient
# The first request after startup compiles kernels; allow more than the default 10 s.
client = TypeSafeClient(base_url="http://localhost:8000", api_key="local", timeout=60)
response = client.system_one(
state={"ticket": "The app crashes every time I open settings."},
questions={
"team": Choice(
instructions="Which team owns this?",
criteria={"billing": "payments, refunds", "technical": "bugs, crashes", "other": None},
),
"bug": Noul(instructions="Is this a bug report?"),
},
)
print(response.answers["team"].choice, response.answers["bug"].noul) # technical 0.92
GET /health reports the loaded checkpoint, whether calibration is active, and the routing settings. The server batches every question in a request into one forward pass, accepts concurrent requests, and returns 422 with the reason for a malformed question.

| subset | task | lev | Jev | always the most common label |
|---|---|---|---|---|
| vitaminc-dev | claim verification | 0.668 | 0.801 | 0.503 |
| massive-en-US | intent routing, 18 scenarios | 0.857 | 0.874 | 0.163 |
| massive-de-DE | intent routing, German | 0.823 | 0.871 | 0.163 |
| boolq | yes/no reading comprehension | 0.827 | 0.893 | 0.580 |
| squad2 | answerability | 0.813 | 0.836 | 0.502 |
| paws | adversarial paraphrase | 0.776 | 0.900 | 0.516 |
| multinli | natural language inference | 0.890 | 0.836 | 0.361 |
| civil_comments | toxicity | 0.760 | 0.803 | 0.893 |
| aegis2 | safety moderation | 0.800 | 0.804 | 0.568 |
| helpsteer2 | helpfulness, 5 levels | 0.386 | 0.341 | 0.422 |
| summeval-relevance | summary relevance, 5 levels | 0.358 | 0.358 | 0.458 |
| summeval-consistency | summary faithfulness, 5 levels | 0.271 | 0.812 | 0.840 |
| pubmedqa | biomedical yes/no/maybe | 0.732 | 0.764 | 0.532 |
| macro | 0.689 | 0.761 |
lev and TypeSafe Jev ran through the same harness on all 3,880 items S1Bench scores, pinned by Nimble's manifests. Our Jev run lands within 0.8 points of TypeSafe's published figure on every subset, so the harness is not the gap.


A held-out split of the 29 training sources, with no row shared with training:
| metric | value |
|---|---|
| weighted accuracy | 0.807* |
| expected calibration error | 0.061* (0.180 before calibration) |
| banking77 (77 intents) | 0.980 |
| clinc_oos (151 intents) | 0.968 |
| FEVER claim verification | 0.872* |
* Measured on this checkpoint before the last serving update. That update routes choice sets of more than 68 options to label-token readout and re-selects the temperatures. The banking77 and clinc_oos rows come from after the update, which raised banking77 from 0.818. Smaller option sets are routed the same way as before.

A call is one batched forward pass over every question, so compute stays flat from one question to eight, and a 60-option choice costs the same as a yes/no.
The 69 ms is engine compute for a short request (a three-sentence state), measured inside the container; S1Bench's longer states take more. End to end from a laptop, Jev's hosted API answered in 335–346 ms median and lev on one Modal H100 in 414–654 ms across two runs.

Text classification in Interfaze runs on a similar system to lev: the model reads the answer from the set of labels you define. The difference is that Interfaze is still token based, so it's a hybrid.
| Item | lev | Interfaze |
|---|---|---|
| Output | Probabilities, zero output tokens | Tokens, returned as structured output |
| Questions | Typed only: yes/no, choice, score | Any JSON schema, labels included |
| In the same request | Classification only | OCR, web search, transcription, extraction, and more |
| Where it runs | Your GPU | Interfaze API |
Tokens cost a little speed, but they let one request classify a document while also reading, searching, and extracting from it. Define your labels as an enum in the schema, and the label comes back as a typed field.
Interfaze docs → interfaze.ai/docs
| file | role |
|---|---|
adapter_model.safetensors, adapter_config.json | LoRA adapter |
mode_b_head.pt | candidate-path head (tensor state dict, loaded with weights_only=True) |
tokenizer*, chat_template.jinja | the tokenizer that the label codes were verified against |
calibration.json | fitted temperatures |
lev_release.json | manifest: base model, prompt format, readout, training step |
The adapter is released under Apache-2.0, the same license as the base model. Some of the training datasets have their own terms, including non-commercial licenses. Review them before commercial use.
Not affiliated with or endorsed by TypeSafe AI.
Apache 2.0 · Interfaze
lev answers typed questions about a piece of context in a single forward pass. You give it a state (text, a ticket, an email, or JSON) and a set of yes/no, choice, and score questions. It reads each answer from the logits it already computed and returns calibrated probabilities over exactly the options you supplied. It is a LoRA adapter on Qwen3.5-4B, and it speaks TypeSafe's /v1/systemone protocol, so code written for the TypeSafe SDK works against it once you change the base URL.
98
8 commits
2 linked in READMEs
updated Sep 25, 2026
lev answers typed questions about a piece of context in a single forward pass. You give it a state (text, a ticket, an email, or JSON) and a set of yes/no, choice, and score questions. It reads each answer from the logits it already computed and returns calibrated probabilities over exactly the options you supplied. It is a LoRA adapter on Qwen3.5-4B, and it speaks TypeSafe's /v1/systemone protocol, so code written for the TypeSafe SDK works against it once you change the base URL.
Qwen3.5-4B + LoRA on one H100. On the six subsets the public S1Bench board completed, level with reflex-4b and behind only Jev and three open models of 26B–35B.
Quickstart · Self-hosting · Benchmarks · Speed · Why it works · Training · Boundaries · GitHub
No generated tokens, no JSON to parse, no retries. lev cannot return a label outside your options, because the answer space is the option set you send. It can still pick the wrong option: the guarantee is structural, not a guarantee of correctness.
| Question | You give | You get |
|---|---|---|
noul | a yes/no question | noul = p(yes) |
choice | instructions + options (name → description or null) | choice, probabilities, confidence |
score | instructions + 2–10 ordered levels | score (expected level), probabilities, confidence |
It is built for the high-volume judgement calls inside a product: routing, moderation, intent detection, triage, grading, and checking LLM output.
pip install "lev[serve] @ git+https://github.com/Abhinavexists/lev#subdirectory=packages/lev"
This needs Python 3.12 or newer and, for real-time use, a CUDA GPU. The serve extra installs torch, transformers, peft, and the HTTP server. The first load downloads the base model (Qwen/Qwen3.5-4B, about 8 GB) and this adapter (about 200 MB).
import lev
model = lev.load("interfaze-ai/lev")
state = "Hi, I was charged twice for my order #4471 and I want a refund."
questions = {
"intent": {
"type": "choice",
"instructions": "What does the customer want?",
"criteria": {
"refund": "wants money back",
"cancel": "wants to cancel an order",
"track": "wants to know where an order is",
"other": "anything else",
},
},
"urgent": {"type": "noul", "instructions": "Does this need a human within the hour?"},
"frustration": {
"type": "score",
"instructions": "How frustrated is the customer?",
"criteria": ["calm", "mildly annoyed", "annoyed", "angry"],
},
}
result = model.system_one(state, questions)
print(result.answers["intent"].choice) # refund
print(result.answers["intent"].probabilities)
# {'refund': 0.84, 'cancel': 0.094, 'track': 0.012, 'other': 0.054}
print(result.answers["urgent"].noul) # 0.43
print(result.answers["frustration"].score) # 1.57, between "mildly annoyed" and "annoyed"
print(result.usage.output_tokens) # 0
These are real outputs from this checkpoint. The answers are objects, and result.model_dump() gives the same JSON the HTTP server returns.
All the questions share one forward pass, so asking three questions costs about the same as asking one. lev.load reads lev_release.json from this repository to find the base model and the prompt format the adapter was trained with. It also applies the shipped calibration and loads the matching head, so there is nothing to configure.
Because the probabilities are calibrated, you can gate on them. For example, act automatically above 0.9 and send anything lower to a person.
lev serve --checkpoint interfaze-ai/lev --host 0.0.0.0 --port 8000
curl -s localhost:8000/v1/systemone -H 'content-type: application/json' -d '{
"state": "The package arrived crushed and the screen is cracked.",
"questions": {"damaged": {"type": "noul", "instructions": "Was the item damaged?"}}
}'
Existing TypeSafe clients work once you point them at the server:
from typesafe_sdk import Choice, Noul, TypeSafeClient
# The first request after startup compiles kernels; allow more than the default 10 s.
client = TypeSafeClient(base_url="http://localhost:8000", api_key="local", timeout=60)
response = client.system_one(
state={"ticket": "The app crashes every time I open settings."},
questions={
"team": Choice(
instructions="Which team owns this?",
criteria={"billing": "payments, refunds", "technical": "bugs, crashes", "other": None},
),
"bug": Noul(instructions="Is this a bug report?"),
},
)
print(response.answers["team"].choice, response.answers["bug"].noul) # technical 0.92
GET /health reports the loaded checkpoint, whether calibration is active, and the routing settings. The server batches every question in a request into one forward pass, accepts concurrent requests, and returns 422 with the reason for a malformed question.

| subset | task | lev | Jev | always the most common label |
|---|---|---|---|---|
| vitaminc-dev | claim verification | 0.668 | 0.801 | 0.503 |
| massive-en-US | intent routing, 18 scenarios | 0.857 | 0.874 | 0.163 |
| massive-de-DE | intent routing, German | 0.823 | 0.871 | 0.163 |
| boolq | yes/no reading comprehension | 0.827 | 0.893 | 0.580 |
| squad2 | answerability | 0.813 | 0.836 | 0.502 |
| paws | adversarial paraphrase | 0.776 | 0.900 | 0.516 |
| multinli | natural language inference | 0.890 | 0.836 | 0.361 |
| civil_comments | toxicity | 0.760 | 0.803 | 0.893 |
| aegis2 | safety moderation | 0.800 | 0.804 | 0.568 |
| helpsteer2 | helpfulness, 5 levels | 0.386 | 0.341 | 0.422 |
| summeval-relevance | summary relevance, 5 levels | 0.358 | 0.358 | 0.458 |
| summeval-consistency | summary faithfulness, 5 levels | 0.271 | 0.812 | 0.840 |
| pubmedqa | biomedical yes/no/maybe | 0.732 | 0.764 | 0.532 |
| macro | 0.689 | 0.761 |
lev and TypeSafe Jev ran through the same harness on all 3,880 items S1Bench scores, pinned by Nimble's manifests. Our Jev run lands within 0.8 points of TypeSafe's published figure on every subset, so the harness is not the gap.


A held-out split of the 29 training sources, with no row shared with training:
| metric | value |
|---|---|
| weighted accuracy | 0.807* |
| expected calibration error | 0.061* (0.180 before calibration) |
| banking77 (77 intents) | 0.980 |
| clinc_oos (151 intents) | 0.968 |
| FEVER claim verification | 0.872* |
* Measured on this checkpoint before the last serving update. That update routes choice sets of more than 68 options to label-token readout and re-selects the temperatures. The banking77 and clinc_oos rows come from after the update, which raised banking77 from 0.818. Smaller option sets are routed the same way as before.

A call is one batched forward pass over every question, so compute stays flat from one question to eight, and a 60-option choice costs the same as a yes/no.
The 69 ms is engine compute for a short request (a three-sentence state), measured inside the container; S1Bench's longer states take more. End to end from a laptop, Jev's hosted API answered in 335–346 ms median and lev on one Modal H100 in 414–654 ms across two runs.

Text classification in Interfaze runs on a similar system to lev: the model reads the answer from the set of labels you define. The difference is that Interfaze is still token based, so it's a hybrid.
| Item | lev | Interfaze |
|---|---|---|
| Output | Probabilities, zero output tokens | Tokens, returned as structured output |
| Questions | Typed only: yes/no, choice, score | Any JSON schema, labels included |
| In the same request | Classification only | OCR, web search, transcription, extraction, and more |
| Where it runs | Your GPU | Interfaze API |
Tokens cost a little speed, but they let one request classify a document while also reading, searching, and extracting from it. Define your labels as an enum in the schema, and the label comes back as a typed field.
Interfaze docs → interfaze.ai/docs
| file | role |
|---|---|
adapter_model.safetensors, adapter_config.json | LoRA adapter |
mode_b_head.pt | candidate-path head (tensor state dict, loaded with weights_only=True) |
tokenizer*, chat_template.jinja | the tokenizer that the label codes were verified against |
calibration.json | fitted temperatures |
lev_release.json | manifest: base model, prompt format, readout, training step |
The adapter is released under Apache-2.0, the same license as the base model. Some of the training datasets have their own terms, including non-commercial licenses. Review them before commercial use.
Not affiliated with or endorsed by TypeSafe AI.
Apache 2.0 · Interfaze