JEVANY / DOCUMENTATION
Train your own decision model
The trainer fits a LoRA adapter with either a pointer or direct-token readout over a frozen open backbone.
Training rows use the same state and questions as inference, plus labels.
The output directory can immediately be loaded by JevModel or jevany serve.
For a complete train/evaluate/resume example and loss/head ablations, see the
small research experiment.
Start with a small backbone
From the repository root, with Python 3.12 or newer:
python -m pip install -e '.[train]'
jevany data init --out data/starter
jevany data validate data/starter/train.jsonl
jevany train --config recipes/sft.toml --dry-run
jevany train --config recipes/sft.toml
The starter has 24 original synthetic training tickets and 8 separate development
tickets, each covering choice, noul, and score, to demonstrate the workflow.
The recipe uses Qwen/Qwen3.5-0.8B, BF16 CUDA weights, LoRA rank 16 and three epochs.
Replace data with your own labelled JSONL and choose a backbone that fits your
device. For a CPU experiment, override the CUDA settings:
jevany train --config recipes/sft.toml \
--device cpu --dtype fp32 --weights-dtype fp32 --out runs/cpu-jev
CPU training is practical for small experiments, and can be slow. The full frozen backbone, adapters, activations and optimizer memory must fit on each device. DDP keeps a full model copy on each GPU and increases throughput by distributing records.
Recipes and overrides
Recipes are flat TOML files containing the trainer's argument names. Paths are
relative to the current working directory. Command-line flags override recipe
values. Unknown keys, invalid types, invalid labels and existing output directories
fail before weights load. --dry-run also checks local base paths and evaluation
suite partitions. Token limits and backbone compatibility are checked when
training starts; these startup checks finish before the output directory is
created, so a corrected input can reuse the intended output path.
data_admission.json records the input, admitted and rejected row counts, with
the ID and reason for each row rejected by context limits. Other encoding errors
stop the run. training_metrics.json repeats the input-scale counts as
input_records, admitted_records and rejected_records. requested_records
is the planned number of input presentations across all configured epochs;
records_seen counts the examples actually processed, including augmentations
and distributed padding. These differ when a run stops early.
| Recipe | Starting point | Output |
|---|---|---|
sft.toml |
Open Qwen3.5-0.8B base | runs/my-jev |
finetune.toml |
Released Qwen3.8-27B LoRA adapter | runs/domain-jev |
rlcr.toml |
runs/my-jev from the small SFT recipe |
runs/my-jev-rlcr |
Fine-tune the released checkpoint with your data:
python -m pip install -e '.[train,multimodal]'
jevany train --config recipes/finetune.toml \
--data data/my-domain.jsonl --out runs/domain-jev
init_from restores the adapter and head into a new training run, with a new
optimizer and schedule. The base revision,
LoRA rank and targets, head dimension, and multimodal settings must match.
The released checkpoint uses the native vision path even for text requests.
For Python applications:
from jevany.training import train
checkpoint = train("recipes/sft.toml", output_dir="runs/python-jev")
All trainer arguments are available through jevany train --help
and python -m jevany.train. Both --head_dim and --head-dim spellings work.
Pointer and direct-token readouts
The default --decision-mode pointer projects the decision-marker state and
each option representation through a compact learned head. It supports more
than 255 choices, with the practical limit set by the context window, and only
computes logits for the supplied options.
--decision-mode lm_token prefixes options with a frozen table of 255 distinct
single-token labels and scores those labels through the base model's original
frozen output head. It gives the highest JevBench accuracy among the released 4B
models, but training is slower because cross-entropy is normalized over the full
vocabulary. It supports at most 255 choices. Both readouts use one backbone prefill
without answer-token generation, so their inference speed should be similar for
comparable inputs.
jevany train --config recipes/sft.toml --decision-mode lm_token \
--lora 8 --lora-dropout 0 --out runs/direct-token-jev
Data beyond the starter
The installable builders convert public sources into the same JSONL format:
jevany data build-sft --help
jevany data build-rlcr --help
The SFT builder covers HelpSteer3, Hermes tool decisions, ARC, QASC, CommonsenseQA, ScienceQA, A-OKVQA, and VideoFeedback, with AI2D/MMMU reserved for evaluation. It downloads upstream data and writes source revisions, licenses, split counts and checksums in its manifest. Defaults include large datasets and media; inspect its options before starting a build.
A text-only build with 500 examples from ARC/QASC/CommonsenseQA:
jevany data build-sft --out data/text \
--helpsteer 0 --agent 0 --hard-text 500 \
--scienceqa 0 --aokvqa 0 --video 0 --eval-per-source 0 \
--tokenizer Qwen/Qwen3.5-0.8B
jevany train --config recipes/sft.toml \
--data data/text/train.jsonl --out runs/text-jev
This command writes only a training split; bring a separate evaluation set.
Training from --suite additionally requires the base revision to be pinned
by the suite or by --base-revision. Full data fields, licenses and the
release mixtures are documented in DATA.md.
Multiple GPUs and hosts
Use one process per visible GPU on a workstation:
bash infra/train.sh --config recipes/sft.toml --out runs/distributed-jev
The same launcher works inside a scheduler allocation. On every host, set
NNODES, its distinct NODE_RANK, and MASTER_ADDR to the rank-0 host.
Mount the same input data, base weights and output directory on all hosts.
Keep the GPU visibility assigned by the scheduler.
The launcher defaults to all visible GPUs and prints the process topology.
Each worker reports its rank, world size, CUDA device and visible device mask.
When using fewer processes, set PROCESSES_PER_HOST and GPU_SHORTFALL_REASON.
Check each worker's device binding against the scheduler allocation.
Allocate resources on your workstation or scheduler before running the launcher.
Select SFT or RLCR with the same
recipes used for a single GPU.
Evaluation and checkpoints
Add --checkpoint-every-steps 100 to save progress every 100 optimizer steps.
Saving works independently of evaluation, so a validation suite is optional.
For your own labelled validation data, add:
jevany train --config recipes/sft.toml \
--eval-data data/starter/development.jsonl --eval-every-steps 10 \
--checkpoint-every-steps 5 --out runs/validated-jev
Validation JSONL uses the same format as training data. It must contain held-out
requests; overlapping training requests are rejected. This path reports the
model's probabilities directly, without fitting temperature on the validation
set. calibration_fitted is false and the additional temperature is 1.
Overlong validation records are listed as rejected in evaluation coverage; use
those counts when comparing results across different context limits.
For a frozen suite, use --eval-suite data/eval-suite --eval-every-steps 100.
The suite needs a manifest plus separate calibration and development splits.
Temperature is fitted on calibration and measured on development.
Use --early-stop-patience 3 for early stopping and --wandb-project for optional
W&B logging.
The final directory contains the adapter, tokenizer, head.pt,
training_config.json, data_admission.json, and training_metrics.json. If early stopping selects a
different checkpoint, selection.json records that path; the root directory
always holds the final weights. Serve the selected path explicitly.
Resume an interrupted experiment
New checkpoints also contain trainer_state.pt and trainer_state.json.
They preserve the optimizer, learning-rate scheduler, shuffle order, next batch,
per-rank random state and accumulated metrics. Saving happens at a complete
optimizer step, after all accumulated gradients have been applied.
jevany train --resume runs/validated-jev/checkpoints/step-000005 \
--out runs/validated-jev-resumed
The saved recipe is restored automatically. Choose a new output directory to retain the original experiment and its checkpoints. The remaining schedule uses the original total step count, learning rates, data, augmentations and evaluation settings. You can change the checkpoint interval, W&B settings and local base mirror path. The world size and device type must stay the same; use the same software and hardware for reproducible numerical results.
Resume checks the admitted records, labels, local media contents and checkpoint
weights against saved fingerprints. Keep local media at their original paths;
paths are part of the saved data identity. Remote media URLs must continue to identify
the same content. If early stopping previously selected another checkpoint, keep
that directory available: a resumed run can still select those earlier weights.
Older weights-only checkpoints and completed schedules can start a new
experiment with --init-from; they cannot resume an unfinished schedule.
The Python entry point jevany.training.train(config) accepts resume in its
TOML recipe as well. Invalid settings raise ValueError, so notebooks and
experiment drivers can catch them without exiting the interpreter.
SFT is the default recipe. RLCR is an experimental continuation with calibration rewards. Measure task accuracy and calibration on your own held-out data before selecting a checkpoint. See the objective and release results.
Backbone support
The implementation uses PyTorch, Transformers and PEFT. The text adapter loads the base component of a Transformers causal language model, adds a pointer head, and trains LoRA plus any newly added decision-token embeddings. The same trainer, checkpoint format and serving API apply across model families.
The model catalog pins 26 official checkpoints across seven families. It records each repository, revision, series, size and native image/video support. Base, Thinking and quantized variants are not additional entries in this catalog.
| Family | Models | Checkpoints |
|---|---|---|
| Qwen | Qwen3.8-27B; Qwen3.6-27B; Qwen3.6-35B-A3B; Qwen3.5-0.8B; Qwen3.5-2B; Qwen3.5-4B; Qwen3.5-9B; Qwen3.5-27B; Qwen3.5-35B-A3B | 9 |
| Gemma | gemma-4-E4B-it; gemma-4-12B-it; gemma-4-31B-it | 3 |
| Muse | Muse-Glimmer-30B | 1 |
| Mistral | Ministral-3-3B-Instruct-2512-BF16; Ministral-3-8B-Instruct-2512-BF16; Ministral-3-14B-Instruct-2512-BF16; Devstral-Small-2-24B-Instruct-2512 | 4 |
| GLM | GLM-4.7-Flash; GLM-4.6V-Flash | 2 |
| Nemotron | NVIDIA-Nemotron-3.5-Lightning-30B-A3B-BF16; NVIDIA-Nemotron-3-Nano-4B-BF16; NVIDIA-Nemotron-3-Nano-30B-A3B-BF16 | 3 |
| Llama | Llama-3.2-1B-Instruct; Llama-3.2-3B-Instruct; Llama-3.2-11B-Vision-Instruct; Llama-3.1-8B-Instruct | 4 |
All families share BackboneAdapter, recipe flags, checkpoint loading and the
serving API. Choose a publisher repository or a local weights directory with
--base; there is no family-specific trainer. Native media selection reads
config.json, so local snapshots work without depending on repository names.
Gemma 4 Unified and GLM 4.6V use their native Transformers models and processors.
Every GPU holds the full base, including all MoE experts. Estimate weight memory
from total parameter count. The small starter remains
Qwen/Qwen3.5-0.8B. Gated repositories require the publisher's license acceptance
and Hugging Face access before downloading; local snapshots need no account.
Phi, Qwen 2.5/3/3-VL/3-Coder, Gemma 3/3n, Pixtral and older Mistral/Magistral releases are outside the maintained scope. The Phi conversion utilities and legacy media adapter names have been removed. The generic text adapter and custom adapter interface are available for experimenting with other architectures.
The released JevAny-Qwen3.8-27B adapter identifies Qwen/Qwen3.8-27B
as its base in the release manifest and adapter configuration.
Each released adapter must be loaded with its own recorded base and revision.
Select the base in the existing recipe or on the command line:
jevany train --config recipes/sft.toml \
--base zai-org/GLM-4.7-Flash --out runs/glm-decisions
The optional adapter settings are:
backbone_adapter = "auto" # chooses the native media adapter when multimodal=true
branch_mode = "auto" # packed where supported; independent causal rows otherwise
lora_targets = "all" # supported linear modules, preserving the existing Qwen layout
# lora_target_modules = "q_proj,v_proj" # explicit names override the preset
rows repeats the shared state for each question. It preserves isolation without
requiring a custom attention mask and supports sliding-window and recurrent
backbones. packed requires an adapter that declares support for that mask;
unsupported combinations fail before training. Option isolation requires packed
mode. Prefix caching is enabled only for validated FP32 backbones. BF16/FP16
models and Qwen's qwen3_5_text recurrent architecture use full-forward inference:
real Qwen 27B and Llama 8B checks found excessive BF16 drift when reusing prefix
states. Serving selects full-forward inference automatically; unsupported direct
prefix-cache calls fail explicitly.
attn includes GLM's low-rank query/key/value projections, Nemotron's Mamba
input projection, and fused QKV projections. A fused QKV layer
cannot apply the qv preset to Q and V independently; use attn, all, or explicit
module names. Explicit names are checked against the loaded model, so a misspelled
target does not silently produce a partial adapter.
LoRA targets are resolved from actual nn.Linear and Conv1D modules. Fused
expert tensors and GLM/Nemotron routers stay frozen. Nemotron's Mamba out_proj
is excluded because its fused training kernel bypasses the module's forward
method. With PEFT 0.21, GLM's dense/shared MLP projection names are remapped to
fused expert parameters even when fully qualified; GLM therefore uses attention
LoRA for all, dense, and attn. Explicit requests for these incompatible
targets fail with an error. Neither model enables packed masks or prefix reuse.
Nemotron can use Transformers' PyTorch Mamba fallback without optional CUDA
kernels; large runs benefit from the upstream optimized kernels.
Non-Qwen tokenizers receive five <|jev_*|> tokens. Their embeddings are
initialized from the base embedding mean and trained automatically, including
when the base has spare vocabulary rows. Other embedding rows stay frozen.
Checkpoints save the tokenizer and learned token rows along with LoRA and the
pointer head. Loading restores that tokenizer before constructing the model.
Gemma E4B also has a frozen per-layer token table. New rows in that table use its
original mean embedding on both initial training and checkpoint reload.
Older Qwen checkpoints continue to use their original delimiters and metadata
defaults.
For another architecture, first try the text adapter. Unknown architectures use
independent rows by default. To customize loading, LoRA selection, packed-mask
support, cache construction, or native media processing, subclass
BackboneAdapter in an installed Python module:
from jevany.backbones import BackboneAdapter
class MyAdapter(BackboneAdapter):
def supports_packed(self, config):
return False
Set backbone_adapter = "my_package.adapters:MyAdapter" in the recipe. The import
path is saved in the checkpoint; install the same trusted adapter package in the
serving environment. Text backbones must accept token IDs, position IDs and an
attention mask and expose last_hidden_state and input embeddings. Native
media processing is handled by the vision adapters below.
Native multimodal training
Set multimodal = true and choose a native vision base. The data format, training
loop, LoRA settings and checkpoint loading stay the same:
| Family | Vision base | Adapter | Media |
|---|---|---|---|
| Qwen | All nine Qwen 3.5, 3.6 and 3.8 catalog entries | qwen_vl |
Images, video |
| Gemma | Gemma 4 E4B, 12B Unified, 31B IT | gemma4_vision |
Images, video |
| Muse | meta-models/Muse-Glimmer-30B |
muse_vision |
Images, video |
| Mistral | Ministral 3 3B/8B/14B, Devstral Small 2 24B | mistral_vision |
Images |
| Llama | meta-llama/Llama-3.2-11B-Vision-Instruct |
llama_vision |
Images |
| GLM | zai-org/GLM-4.6V-Flash |
glm_vision |
Images, video |
auto selects from the base's configuration. A text-only variant does not gain
vision support by setting the flag. Unsupported media types fail with an error.
Audio is not part of the current training/request format.
Muse's native processor expands image/video placeholders and supplies frame
timestamps. Its adapter caps images at 512 merged tokens per record and videos
at eight sampled frames with a per-frame token budget. Processor metadata is used
during encoding and is not passed to the decoder.
For example, on a local CUDA GPU:
python -m pip install -e '.[train,multimodal]'
jevany train --config recipes/sft.toml --multimodal \
--base google/gemma-4-31B-it --data data/images.jsonl --out runs/gemma-vision \
--lr 0.00002 --head-lr 0.00002
Each media record contains one question. Native media records do not support
option_isolation or prefix-cache reuse. Relative media paths resolve against the
JSONL file; use the same labelled request format as text training:
{"state":"Inspect the sample.","media":[{"type":"image","uri":"red.png"}],"questions":{"color":{"type":"choice","instructions":"What color is the sample?","criteria":{"red":"Red","blue":"Blue"},"label":"red"}}}
Training freezes the existing vision encoder and projector, and fits the language decoder's LoRA, decision head and added decision-token embeddings. The native processor handles image token expansion and model-specific inputs such as Llama's cross-attention mask and Gemma's token types. Checkpoints save the complete processor and selected adapter, so serving and subsequent training use the same encoding. Text-only and image records can share a run; DDP accounts for Llama's cross-attention parameters being unused on text-only records.
Devstral's official FP8 weights are dequantized for BF16 LoRA training, including on A100 GPUs. Ministral's listed release already contains BF16 weights. Both use the upstream tokenizer regex correction. The text adapter can extract a language decoder from a composite vision base for text-only training.
For another native vision architecture, subclass VisionAdapter from
jevany.backbones. Set media_types and language_model_path; override
process_media or forward_media for another processor/model protocol.
encode_media can handle architectures needing different readout positions.
Set conditional_parameters = True if some trainable decoder paths are absent
on text-only records. Select the installed class through the existing
backbone_adapter = "my_package.adapters:MyVisionAdapter" setting. Models return
token hidden states for the shared decision head; the trainer does not contain
family-specific forward branches.
Both local training and torchrun use these interfaces without a cloud account
or resource-provider SDK.
Short compatibility checks
The pretrained validation record includes all
26 current catalog checkpoints with 80 passing input/objective checks. The
original 37-checkpoint record is retained as historical evidence, including
models since removed from the maintained scope. It includes
revisions, learning rates, precision, reload errors, serving checks and failed
attempts. Most SFT checks use 12 steps, followed by two RLCR steps. The Gemma 4
12B video check uses 48 SFT steps, --dtype fp32, --lr 2e-7 and
--head-lr 1e-4, with BF16 frozen weights.
The catalog contract test checks that every maintained checkpoint has matching revision, modality, SFT, RLCR, reload and Python/HTTP serving evidence. Media checks include mixed text records.
The offline tests use real, tiny Transformers architectures for the selected families, plus a GPT-2 fixture for the generic custom-backbone contract. They cover sliding-window and recurrent layers, MoE forwards, LoRA selection and gradients, added-token embeddings, branch isolation, supported cache reuse, native media, checkpoint reload, and SFT-to-RLCR continuation:
python -m pytest tests/test_model_support.py tests/test_backbones.py -q
python -m pytest tests/test_multimodal_backbones.py tests/test_serving.py tests/test_smoke_backbone.py -q
To run a short pretrained-weight check:
python -m pip install -e '.[dev]'
python -m scripts.smoke_backbone \
--base Qwen/Qwen3.5-0.8B --steps 12 --out runs/smoke-qwen
TORCH_NCCL_USE_COMM_NONBLOCKING=0 \
torchrun --standalone --nproc_per_node=2 --module scripts.smoke_backbone \
--base Qwen/Qwen3.8-27B --steps 12 --lr 0.00002 --head-lr 0.00002 \
--out runs/smoke-qwen-ddp
Use --revision to pin weights and --device cpu for a sufficiently small base.
Use --base-load-path /path/to/snapshot to load a predownloaded copy while keeping
the official --base and --revision in the checkpoint.
The smoke script exposes --lr and --head-lr for bases with different gradient
scales; its default head learning rate is 1e-4.
Use --dtype fp32 to disable training autocast while retaining BF16 frozen
weights on CUDA; the report records this separately from the checkpoint's weight dtype.
The script runs the actual trainer, measures uncalibrated NLL before and during
training, reloads the checkpoint, and checks predictions through both the Python
runtime and the FastAPI application. The HTTP checks exercise health, model
description, repeated typed requests, and rejection of invalid requests. It checks
prefix-cache parity where supported and explicit rejection elsewhere.
It writes smoke.json, per-rank GPU identity, and the trainer's evaluation
history. A passing SFT run requires finite adapter weights, updated LoRA weights,
lower final NLL, and successful prediction checks. Its small evaluation probe
reuses training examples to check optimization and compatibility.
The probe covers all eight training rows,
including both media and text rows when --mixed-text is enabled. Media probes
score each row in both choice orders, matching the trainer's option permutation.
To check RLCR continuation and serving from the resulting checkpoint:
python -m scripts.smoke_backbone --base Qwen/Qwen3.5-0.8B \
--init-from runs/smoke-qwen/checkpoint --rlcr --steps 2 --out runs/smoke-qwen-rlcr
RLCR must update the saved LoRA tensors and retain finite losses and predictions. Its combined reward objective does not require the supervised NLL to decrease.
For native media, the same smoke script generates its own image or video files:
python -m scripts.smoke_backbone --base Qwen/Qwen3.8-27B \
--media image --mixed-text --steps 12 --lr 0.00002 --head-lr 0.00002 \
--out runs/smoke-qwen-vision
Use --media video for a video-capable base, or run the same module under
torchrun. The media check also requires predictions to change when the media
changes while the question and state stay fixed.
For PyTorch 2.6 / CUDA 12.6, use TORCH_NCCL_USE_COMM_NONBLOCKING=0.
infra/train.sh sets this default before starting workers
and preserves an explicit override.
PyTorch 2.6 uses DDP's standard initial parameter synchronization; newer versions
with init_sync support can avoid broadcasting the frozen base.
The runtime admits up to 2,048 packed training tokens by default. Multimodal rows
contain one question and require a native vision-capable base and
multimodal = true. Model weights and third-party data keep their upstream
licenses. The complete frozen suites and raw media from the release are not
included, so the default builder alone does not reproduce the published run.