"""Qevi: answer typed questions about an image in one forward pass, without generating text. A normal vision-language model answers a question by decoding tokens one at a time, and re-encodes the image for every question you ask. When the answer is known to be one of a small fixed set ("Yes"/"No", "A"/"B"/"C"/"D", "Poor"/"Fair"/"Good"), both of those costs are waste. Qevi instead encodes the image once, packs every question into a single sequence behind a block-diagonal attention mask that keeps the questions provably isolated from each other, runs one forward pass, and reads the model's own LM-head logits at the position where it would have started writing each answer. Softmax over just that question's candidate tokens gives a calibrated probability. from transformers import AutoProcessor, Qwen3VLForConditionalGeneration from qevi import build_readouts, noul_question, forward_logits, softmax model = Qwen3VLForConditionalGeneration.from_pretrained(MODEL, dtype="bfloat16").cuda() processor = AutoProcessor.from_pretrained(MODEL) specs = [(noul_question("There is a ladder in this image."), ["Yes", "No"])] readouts = build_readouts(processor.tokenizer, specs) logits = forward_logits(model, processor, image, readouts) print(softmax(logits[0])) # -> [0.97, 0.03] Everything here works with a stock Qwen3-VL checkpoint. The fine-tuned weights are simply more accurate through the same path; no custom architecture and no trust_remote_code is involved. """ from .core import ( Readout, build_readouts, forward_logits, softmax, unwrap, wrap_turn, noul_question, choice_question, choice_question_generic, score_question, score_question_generic, NOUL_TEMPLATE, LETTERS, PREFIX_PRE, PREFIX_POST, ) from .pack import Question, PackedBatch, Segment, build_packed_batch, permute_questions __version__ = "0.1.0" # The fine-tuned weights in MeerDevelopment/Qevi-2B are calibrated as-is: label smoothing during # training removed the base model's overconfidence, and T=1.0 minimises ECE on both eval splits. # The older values (noul 2.45, choice 1.9, score 3.0) were fitted against the BASE model and make # the fine-tune noticeably UNDER-confident; do not apply them to these weights. DEFAULT_TEMPERATURES = {"noul": 1.0, "choice": 1.0, "score": 1.0} __all__ = [ "Readout", "build_readouts", "forward_logits", "softmax", "unwrap", "wrap_turn", "noul_question", "choice_question", "choice_question_generic", "score_question", "score_question_generic", "NOUL_TEMPLATE", "LETTERS", "PREFIX_PRE", "PREFIX_POST", "Question", "PackedBatch", "Segment", "build_packed_batch", "permute_questions", "DEFAULT_TEMPERATURES", "__version__", ]