
Indigo Violet 1
Indigo Violet answers typed questions about a text with probabilities: yes/no questions, multiple choice and ordered scores. Every question of a request is answered from one forward pass of a 0.6B-parameter model cut to 18 of its 28 layers, with no text generation.
Violet is the smallest Indigo model. The family is named after purple flowers, from the smallest plant to the largest: Violet, Iris, Lilac, Wisteria.
- Code: https://github.com/rwondo/indigo-violet-1 (the
indigo-decisionsPython package, import nameindigo) - Base model: Qwen/Qwen3-0.6B at revision
c1899de289a04d12100db370d81485cdf75e47ca - Release:
indigo-violet-1, served in float16 on GPU and float32 on CPU
Usage
Install the package from its repository (Python 3.10 or newer, PyTorch 2.4 or newer):
pip install "git+https://github.com/rwondo/indigo-violet-1"
import indigo
model = indigo.load("blockboiarb/indigo-violet-1")
model.decide(
"Order 1182 arrived two weeks late and the customer asks for a refund.",
{
"refund": {"type": "noul", "instructions": "Does the customer ask for a refund?"},
"team": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {"billing": "Refunds and invoices", "shipping": "Deliveries"},
},
"urgency": {
"type": "score",
"instructions": "How urgent is this?",
"criteria": ["Not urgent", "Somewhat urgent", "Very urgent"],
},
},
)
# {"refund": {"type": "noul", "noul": P(yes)},
# "team": {"type": "choice", "probabilities": {"billing": p, "shipping": p}},
# "urgency": {"type": "score", "probabilities": {"0": p, "1": p, "2": p}}}
indigo.load checks the sha256 of the weight files listed in indigo.json, downloads the base
model at its pinned revision (about 1.5 GB), merges the adapter, keeps the first 18 layers and
installs the readout head.
Results
Kaggle T4 GPU, float16, this release's weights and calibration, one request at a time.
| Benchmark | Indigo Violet 1 |
|---|---|
| JevBench-mini accuracy / Brier (1,938 questions with gold answers) | 0.807 / 0.339 |
| JevBench-mini accuracy by type: yes/no, choice, score | 0.876, 0.852, 0.370 |
| jabr classifier-benchmark, micro accuracy (944 cases) | 0.737 |
| jabr accuracy by type: choice, yes/no, score | 0.837, 0.659, 0.682 |
| JevBench-mini latency per request (about 10 questions), p50 / p95 | 49.2 ms / 68.5 ms |
| jabr latency per question, mean | 11.0 ms |
- On the same benchmarks Qwen3-4B-Instruct-2507, run zero-shot and uncalibrated with an earlier version of the same harness, scored 0.850 accuracy and 0.268 Brier on JevBench-mini and 0.793 on jabr. Indigo Violet trades some accuracy for speed: 18 layers of a 0.6B model per request.
- JevBench's coherence score (overall 0.880 [0.867, 0.893]) is not a quality measure on its own: answering every question uniformly scores 0.894 on it.
Training
- Distillation. The teacher, Qwen/Qwen3.5-27B (Apache-2.0), labelled every yes/no probe of the training texts with its yes/no logit gap. The student, Qwen/Qwen3-0.6B with a LoRA adapter (rank 16, alpha 32; down_proj, gate_proj, k_proj, o_proj, q_proj, up_proj, v_proj projections), was trained to match the teacher's gaps (rescaled by a fitted linear map) plus a loss on the datasets' own gold labels. The served model keeps the first 18 layers and reads the gap from a readout head trained on layer 18.
- Data. A training set of 41,501 texts from 27 sources (26 datasets; LEX GLUE gives two). All come from train splits except LegalBench, whose texts come mostly from its test splits (its train splits hold a few examples per task). No text comes from JevBench, jabr, their source datasets or their generators, and the texts were screened against those benchmarks for exact and near-duplicates.
- Held out. 2,981 texts were held out to select the checkpoint: a random 4%, every HotpotQA text and a few question families. The model was trained on the other 38,520 texts.
| Dataset | Licence | Texts in the training set |
|---|---|---|
| ShARC | CC BY-SA 3.0 | 4,628 |
| stasvinokur/cve-and-cwe-dataset-1999-2025 | CC0 1.0 | 3,000 |
| legacy-datasets/banking77 | CC BY 4.0 | 2,490 |
| clinc/clinc_oos | CC BY 3.0 | 2,490 |
| DFKI-SLT/few-nerd | CC BY-SA 4.0 | 2,000 |
| nvidia/HelpSteer2 | CC BY 4.0 | 2,000 |
| coastalcph/lex_glue (LEDGAR) | CC BY 4.0 | 1,997 |
| ucberkeley-dlab/measuring-hate-speech | CC BY 4.0 | 1,750 |
| DoDataThings/us-bank-transaction-categories-v2 | MIT | 1,500 |
| google/boolq | CC BY-SA 3.0 | 1,500 |
| tasksource/esci | Apache-2.0 | 1,500 |
| FuzzyLabs/ntsb-accident-extraction | CC0 1.0 | 1,500 |
| allenai/prosocial-dialog | CC BY 4.0 | 1,500 |
| wenhu/tab_fact | CC BY 4.0 | 1,500 |
| OpenAssistant/oasst2 | Apache-2.0 | 1,499 |
| nguha/legalbench | CC BY 4.0, CC BY-SA 4.0 or MIT, per task | 1,479 |
| nvidia/Aegis-AI-Content-Safety-Dataset-2.0 | CC BY 4.0 | 1,246 |
| fancyzhx/dbpedia_14 | CC BY-SA 3.0 | 1,000 |
| Anthropic/hh-rlhf (red-team attempts) | MIT | 1,000 |
| NOAA NCEI Storm Events Database (2015-2024) | US federal work, public domain | 1,000 |
| coastalcph/lex_glue (UNFAIR-ToS) | CC BY 4.0 | 1,000 |
| nvidia/Nemotron-PII (synthetic) | CC BY 4.0 | 800 |
| nvidia/HelpSteer3 | CC BY 4.0 | 750 |
| hotpotqa/hotpot_qa | CC BY-SA 4.0 | 750 (held out, not trained on) |
| Zhengping/UNLI | MIT (SNLI text: CC BY-SA 4.0) | 750 |
| lasha-nlp/CONDAQA | Apache-2.0 | 474 |
| google/civil_comments | CC0 1.0 | 398 |
Some datasets are share-alike (CC BY-SA): ShARC, Few-NERD, BoolQ, DBpedia-14, HotpotQA, the SNLI text in UNLI and some LegalBench tasks. Whether share-alike terms reach weights trained on such data is unsettled; check them for your use. The HelpSteer prompts come mostly from ShareGPT conversations, and ProsocialDialog was drafted with a GPT-3 model.
How it works
- The LoRA adapter is merged into
Qwen/Qwen3-0.6Bat revisionc1899de289a04d12100db370d81485cdf75e47ca. - Only the first 18 decoder layers are kept. The final norm then applies to layer 18, and
the readout head reads the gap
z_yes - z_no = w . final_norm(h_18) + cat the last token of a yes/no prompt. - Each question becomes yes/no probes. A yes/no ("noul") question is one probe; instructions that
do not end with "?" are asked as
Is the following statement true? {statement}. A choice or score question has one probe per option or level:{instructions}\nCandidate answer: {option}\nIs the candidate answer correct?. - All probes of a request run in one forward pass with a tree attention mask: each probe sees the text and its own question only, so the result is the same as running every probe alone (up to floating-point rounding).
- Calibration turns gaps into probabilities:
P(yes) = sigmoid(8 * gap)for a noul,softmax(gap / 0.125)over the options of a choice andsoftmax(gap / 0.125)over the levels of a score.
The adapter holds LoRA weights for all 28 layers, as trained. Only layers 0 to 17 are served: the weights of layers 18 to 27 are merged and then dropped with those layers, so they have no effect.
Limits
- English. The training texts are almost all English.
- Probabilities. Gaps become probabilities through a fixed sharpening, not a fitted calibration (JevBench-mini Brier 0.339).
- Short texts. Training texts were at most 1,024 tokens. Longer texts run (nothing is truncated) but are outside what the model was trained on.
- Unfamiliar tasks. Accuracy drops on task types unlike the training data, and knowledge-heavy questions (exams, maths, science) are beyond a model this size.
- No truncation. A request whose forward pass (instruction header, text and every probe) is
longer than
max_tokensis refused withTooLongErrorbefore anything runs. The default is 8192 tokens, andmax_tokenscan be raised up to the base model's context length of 40960 tokens. - Saturation: with the choice temperature of 0.125, an option that leads the
others by more than about 4.6 in gap gets a probability of exactly 1.0 in float64. For a ranking
across requests, use the raw gaps (
model.gaps(...)).
Decision Index
With the decision-index extra installed, the package runs as a
Decision Index engine:
pip install "indigo-decisions[decision-index] @ git+https://github.com/rwondo/indigo-violet-1"
python -m decision_index run --engine indigo.decision_index:IndigoEngine --option model=blockboiarb/indigo-violet-1
The engine renders every request the same way, refuses passes over max_tokens as unsupported and
gives every option a probability.
Files
| File | Content | sha256 |
|---|---|---|
adapter_model.safetensors |
LoRA weights (PEFT format) | 33e18bbe133b19e180b7ce3427e4c78f36544ea2ecad2d441349bb1e7d050da9 |
adapter_config.json |
LoRA configuration | 7a19981627759a0200c80cd3a78891c11d51a597b177e760028821822d471a80 |
readout.safetensors |
readout head: w [1024] and c [1], float32 |
53063ede4feae2a52bbb158dcce4b8ee5a6df07a0f304ff91d1d0842aadbb51b |
indigo.json |
base model and revision, depth, prompt, calibration, engine defaults, file digests |
License
The files in this repository are released under the Apache License 2.0 (see LICENSE). The base
model, Qwen/Qwen3-0.6B, is also released under the Apache
License 2.0. The training data keeps its own licences (table above).