Instructions to use AlexWortega/openjev with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use AlexWortega/openjev with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="AlexWortega/openjev")# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("AlexWortega/openjev", device_map="auto") - Notebooks
- Google Colab
- Kaggle
# Load model directly
from transformers import AutoModel
model = AutoModel.from_pretrained("AlexWortega/openjev", device_map="auto")openjev β Qwen3.5 trained as jev model
openjev is Qwen3.5 turned into a jev model: a single cross-encoder that reads a premise and a hypothesis and answers with entailment, contradiction or neutral. That one primitive is enough to rerank answers, grade them against a reference, guard content, and play games in real time: hand it the game state and a few statements about it, and the argmax entailment is the move. Nothing is trained per task.
openjev-4B v2: text, images and agents
The new 4B checkpoint (qwen3.5-4b-nli-v2/) reads images as well as text and was trained on a much larger and harder
mixture. It is strictly zero-shot on everything shown here.
- Doom straight from the pixels (first video): 10.4 kills per episode, twice the v1 model (5.2); random play gets 1.
- Crafts an iron pickaxe from nothing in real Minecraft (second video): 11 milestones in ~22 decisions, driven by a backward-chaining scaffold where the jev model only checks statements about the inventory and the world.
- Much stronger on adversarial NLI (ANLI r3 0.42 β 0.63, WANLI 0.63 β 0.77) and on image claims (0.52 β 0.84), better reranking (ARC-Challenge 0.59 β 0.72, MMLU 0.47 β 0.53), same MNLI (0.91).
Doom from the text state (v2, 11 kills per episode; a perfect-information bot gets 18.8):
Bigger jev: Qwen3.5-35B-A3B (MoE) as the backbone (qwen3.5-35b-a3b-nli/). Zero-shot, and with the backbone frozen plus
a small MLP head on the last-token latent (mlp_heads_35b/, one head per task, loadable with LatentMLPHead.load):
What's inside
qwen3.5-0.8b-nli-v2s-long/β the small v2s checkpoint (0.8B, 4k context): the v2 mixture plus faithfulness / instruction-following / false-premise data and a long-document stage. MNLI 86.2/87.1, ANLI r1 65.1, SciTail 93.2, RAGTruth AUROC 0.90, LLM-AggreFact avg bAcc 69.5. This is what the demo Space serves.qwen3.5-4b-nli-v2/β recommended: the 4B v2 jev checkpoint, text + images.qwen3.5-4b-nli/β the original 4B jev checkpoint (text).qwen3.5-35b-a3b-nli/β the 35B-A3B MoE jev checkpoint (load withmodeling_qwen35_moe_seqcls.py).- All checkpoints:
Qwen3_5ForSequenceClassification, 3 labelscontradiction,entailment,neutral, last-token pooling, trained with plain cross-entropy over the three classes. modeling_openjev.pyβOpenJevCrossEncoder:predict,predict_hypotheses,rerank,grade,latents,latents_hypotheses;LatentMLPHeadfor the per-task heads.modeling_qwen35_moe_seqcls.pyβQwen3_5MoeForSequenceClassificationfor the 35B-A3B backbone.mlp_heads_35b/<task>/βhead.pt+norm.npz+meta.json, the 35B latent + MLP heads behind the second radar.code/β everything used here: the trainer and data mixture builder, the evaluation harness, Flappy Bird, Doom (text and pixels), the Minecraft scaffold and bot, the radar, the SGLang package / launcher / client / benchmark.videos/β Flappy Bird, Doom and Minecraft replays;results/β raw JSON for every run and the full report.
Use it
from modeling_openjev import OpenJevCrossEncoder
jev = OpenJevCrossEncoder("AlexWortega/openjev", subfolder="qwen3.5-4b-nli-v2")
jev.predict([("The bird is 0.05 below the centre of the gap.", "The bird is below the centre of the gap.")])
# -> [[contradiction, entailment, neutral]] probabilities
jev.rerank("Which gas do plants absorb during photosynthesis?", ["oxygen", "carbon dioxide", "nitrogen"])
# -> index of the option with the highest entailment
jev.predict_hypotheses("Which gas do plants absorb during photosynthesis?",
["The correct answer is: oxygen", "The correct answer is: carbon dioxide",
"The correct answer is: nitrogen"])
# -> one [contradiction, entailment, neutral] row per hypothesis
predict_hypotheses and latents_hypotheses use the existing pairwise batch for one or two hypotheses. For three or
more, they compute the common token prefix once, then score every hypothesis in one batched continuation. rerank
uses the same rule. Qwen3.5 has recurrent linear-attention layers, so a 4D packed
tree mask alone would mix branches; the shared prefix cache is copied into separate batch entries for the suffixes.
This path is for text inputs. Its batch size and suffix padding use memory proportional to the number and length of
the hypotheses; split very large option sets into smaller calls.
Or with plain transformers:
from transformers import AutoModelForSequenceClassification, AutoTokenizer
tok = AutoTokenizer.from_pretrained("AlexWortega/openjev", subfolder="qwen3.5-4b-nli-v2")
model = AutoModelForSequenceClassification.from_pretrained("AlexWortega/openjev", subfolder="qwen3.5-4b-nli-v2")
text = model.config.nli_template.format(premise="...", hypothesis="...")
Images go inside the premise as <|vision_start|><|image_pad|>β¦<|vision_end|> with pixel_values / image_grid_thw
from the Qwen3.5 image processor; see code/doom_vision.py and code/eval_image_nli.py.
Serve it with SGLang
SGLang has no sequence-classification class for Qwen3.5, so code/sglang_openjev/ is an external model package
that adds the score head to SGLang's Qwen3_5ForConditionalGeneration (hybrid cache, mrope and the vision tower
stay as they are). Text and images both work; /classify returns the three raw logits.
hf download AlexWortega/openjev --include "qwen3.5-0.8b-nli-v2s-long/*" "code/*" --local-dir openjev
cd openjev/code && bash serve_sglang.sh ../qwen3.5-0.8b-nli-v2s-long 30000 # tested with sglang 0.5.19
from sglang_client import OpenJevSGLang # code/sglang_client.py
jev = OpenJevSGLang("http://127.0.0.1:30000")
jev.predict([("A man is playing a guitar.", "Someone is making music.")]) # [[con, ent, neu]]
jev.predict([("A photograph of a scene:", "There is a dog.")], images=["dog.jpg"])
Without the script: SGLANG_EXTERNAL_MODEL_PACKAGE=sglang_openjev with code/ on PYTHONPATH, then
python -m sglang.launch_server --model-path <dir> --is-embedding --json-model-override-args '{"architectures": ["Qwen3_5ForConditionalGeneration"]}'.
Same predictions as transformers, 1.5-3x the throughput. qwen3.5-0.8b-nli-v2s-long on one RTX A6000 that was
shared with another job (so absolute speed is a lower bound), transformers at batch 32 (code/bench_sglang.py):
| task | pairs | tokens / pair | acc SGLang | acc transformers | SGLang pairs/s | transformers pairs/s | speed-up |
|---|---|---|---|---|---|---|---|
| MNLI m+mm | 19647 | 44 | 86.63 | 86.64 | 321 | 152 | 2.1x |
| ANLI r1 | 1000 | 105 | 65.3 | 64.9 | 189 | 119 | 1.6x |
| ANLI r2 | 1000 | 103 | 50.6 | 50.8 | 181 | 110 | 1.6x |
| ANLI r3 | 1200 | 92 | 48.6 | 48.6 | 218 | 95 | 2.3x |
| WANLI | 5000 | 40 | 73.7 | 73.7 | 400 | 196 | 2.0x |
| SciTail | 2126 | 43 | 93.3 | 93.4 | 319 | 217 | 1.5x |
| ConTRoL (long) | 805 | 612 | 51.9 | 51.4 | 45 | 15 | 3.1x |
| ARC-Challenge (rerank) | 9374 | 49 | 49.2 | 49.2 | 334 | 197 | 1.7x |
| HellaSwag (rerank, 2k questions) | 16000 | 114 | 37.6 | 37.5 | 176 | 109 | 1.6x |
Reference point: dleemiller's NLI cross-encoders. Licence MIT.


# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="AlexWortega/openjev")