Repository navigation
[RFC]: Native Rust/CUDA support for MaestroYan/JEMM and community help #121
Description
Motivation
Add support for MaestroYan/JEMM, an
open-weight Apache-2.0 decision model advertised as "Like Jev, but multimodal
and open-weight." One prefill-only forward pass turns a state (text plus up to
four screenshots), a question, and 2–32 candidates into a probability for every
candidate, for choice, noul, and score questions.
JEMM is a strong fit for this engine and unusually cheap to integrate relative
to the rest of the Jev family:
- It is a rank-16 LoRA adapter on
Qwen/Qwen3.8-27B— the same backbone our
validated Open-Jev-27B native worker already runs on (shared
Qwen3.5/3.8 prefill kernels, merged-checkpoint export machinery). - Its readout is Cua-S1-style last-position label-token logits over 32
labels (A–Z,0–5) with a saved temperature — no trained decision
head, no full-vocabulary features, no branching (unlike Open-Jev-27B,
openjev/openjev [RFC]: Support openjev/openjev in System1-Omni — community help wanted #95, or OmniJev [RFC]: Native Rust/CUDA support for OmniJev-4B v1.1 and community help #114). - Its reference server (ypcypc/JEMM) already
speaks the Jev-stylePOST /v1/systemoneenvelope our frontend forwards, and
is serial by construction, matching our shared serial runtime. - It is multimodal (
Qwen3_5ForConditionalGeneration), andmainnow has
shared CUDA vision entry points (ABI 5) plus native Cua-S1 vision and image
preprocessing in Rust to reuse.
No existing worker can load this checkpoint: Open-Jev-27B's worker compiles one
independent prompt per candidate and scores it with a trained scalar head, while
JEMM renders all candidates into one prompt and reads 32 label tokens at the
final position. This is a separate model directory and recipe, not a config
change.
This RFC invites feedback and contributors; nothing below has been implemented.
Proposed Change
1. Target and reproducible reference
| Artifact | Inspected revision |
|---|---|
| System1-Omni | 4a79980d8a75fd063cb3f8247e06215e288b18ac |
JEMM adapter, decision_config.json |
MaestroYan/JEMM @ 76e3c209e8441fa658221c7ba2725bad2f811176 |
| Reference server and contract code | ypcypc/JEMM @ 6822fe0fd53c5e6670af6ba99fb2c857a661e532 (Apache-2.0) |
| Qwen3.8-27B backbone | 1d4bf0f2ff6012fd82039f2fa52739d0dd7c60c0 — the pin already used by the Open-Jev-27B recipe |
The adapter's adapter_config.json names Qwen/Qwen3.8-27B but does not pin a
base revision ("revision": null); the pin above is proposed because it is the
revision our Open-Jev-27B validation already used. Confirm with the authors and
freeze a known-good environment before generating fixtures. The server's
declared environment is torch>=2.12, transformers>=5.17,<6, peft>=0.21,
accelerate>=1.15, flash-linear-attention>=0.5.2 (Linux), numpy, pillow
— minimum versions, not a lock.
The adapter is LoRA rank 16, alpha 32, dropout 0, stored fp32 by PEFT. Its
targets cover attention and MLP projections and the Gated DeltaNet
projections (in_proj_a, in_proj_b, in_proj_qkv, in_proj_z,
out_proj) — unlike the Cua-S1 text adapter, which leaves the linear-attention
blocks alone. The export must merge all of these.
decision_config.json holds three scalars the worker must load, not hard-code:
temperature = 1.3480874159655591 (text), mm_temperature = 1.3954832341582943
(any image present), threshold = 0.9872681877423998.
First slice: BF16 CUDA, text and screenshots (1–4 images per request), all
three question types, 2–32 candidates, serial admission, served behind the
existing frontend. Out of scope initially: the reference's multi-question
"bundle" prompt, cross-question prefix reuse, quantization, Metal, multi-GPU.
2. Ownership and serving interface
Follow the architecture contracts and the model integration skill:
src/models/jemm/owns the native worker: request compilation, candidate
rendering, tokenization, image preparation, execution, readout, and response
finishing;recipe/jemm/holds export/setup/launch and example requests; all
tests and fixtures live intests/jemm/under the roottests/tree.- Serve
POST /v1/systemoneandGET /healthbehind the existing Rust
frontend unchanged (OMNI_JEV_BACKEND_URLpointing at the JEMM worker). - Wire input matches the reference server:
state(string, or any JSON value
rendered as compact JSON),questionsas an object keyed by question ID, each
withtype(choice/noul/score, defaulting tochoice) andcriteria,
plus an optionalimageslist of up to 4 base64 PNG/JPEG/WebP data URLs. Any
transport adaptation must be documented and tested. - Reuse the shared serial runtime for admission. The reference server is serial
behind a global lock; a native worker must preserve single-request decision
semantics and document its concurrency model. - The serving process requires no Python/PyTorch. A one-time exporter and a
pinned Python reference for fixtures are appropriate.
3. Checkpoint preparation and execution reuse
- Reuse the Open-Jev export pattern (
recipe/open_jev/export_merged.py): merge
the LoRA into the 27B backbone in BF16 (CPU, roughly the same ~110 GB RAM /
~52 GB storage class as Open-Jev-27B, plus vision weights), and save the
decision_configscalars, tokenizer/processor files, and chat-template
arguments in a manifest (e.g.jemm_export.json). The worker refuses a plain
base checkpoint or an incomplete export. - Qualify BF16 LoRA merging against the unmerged fp32-PEFT reference before
adopting it (same concern as [RFC]: Native Rust/CUDA support for OmniJev-4B v1.1 and community help #114). The merge must cover the GDN projections
listed above. - Verify input/output embedding tying for this checkpoint from its config
before reading label rows (see the [RFC]: Support openjev/openjev in System1-Omni — community help wanted #95 warning about untied heads in this
family). - Reuse the shared Qwen3.5/3.8 prefill kernels and layer loop
(src/models/qwen3_5/native/,src/backends/cuda/qwen3_5/). For images,
reuse the shared vision entry points and the native Cua-S1 vision /
image-preprocessing pipeline where the 27B vision tower configuration
permits; CPU image preparation must match the baseAutoProcessorexactly
(decode, RGB, resize, patch order, placeholder expansion, 3D positions).
Existing Cua-S1, Open-Jev-27B, and Open-Jev-9B regression checks must remain
intact; any ABI change bumps the version and rebuilds consumers together. - GPU requirement follows the backbone: BF16 27B plus vision, one CUDA GPU with
≥64 GB as the reference states; compute capability 8.0+ per the shared
kernels.
4. JEMM semantics to preserve
The pinned normative reference is the server code (ypcypc/JEMM@6822fe0),
not the model-card snippet: where they differ, the server wins, and known
differences must be recorded.
| Boundary | Required behavior |
|---|---|
| Candidate mapping | choice: criteria map, ≥2 options, candidate IDs are stringified map keys in caller (JSON insertion) order. noul: fixed yes/no IDs, descriptions from criteria.true/criteria.false or the defaults "yes"/"no". score: criteria list of ≥2 levels, IDs "0"… in order, description or level N. Missing type defaults to choice; 2–32 candidates; duplicate IDs rejected. |
| Rendering | 32 labels A–Z then 0–5. Question and candidate descriptions are whitespace-collapsed; state is not collapsed — only .strip(), with non-string state rendered as compact json.dumps (ensure_ascii=False, separators=(",",":")). Candidates render as A) <description>; a candidate whose action carries tool_name has its description parsed as a JSON tool spec and rendered in the reference's name — summary | params: a*, b=default form (defaults truncated to 24 chars). |
| Prompt | Fixed system message; user text in the exact State:/Question:/Candidates:/Answer with exactly one candidate label. layout; images precede the text as separate content blocks in one user message. Chat template applied with add_generation_prompt=True, enable_thinking=False, preserve_thinking=False from the base processor/tokenizer. |
| Limits | 8192 prompt tokens text-only; 3072 with any image (budget includes image tokens); ≤4 images; each image ≤ 3072×32×32 = 3,145,728 pixels; PNG/JPEG/WebP only, RGB; request body ≤ 16,000,000 bytes. Oversize or malformed inputs fail before inference with the reference error wording (HTTP 422, {"error": <type>, "detail": <message>}). |
| Readout | One forward per question, logits_to_keep=1, last position. Label token IDs are the first token of each label string via encode(add_special_tokens=False). Verify all 32 labels are single tokens under the pinned tokenizer and fixture them. Selected logits cast to FP32. |
| Calibration | Probability computation follows the server: numpy float64 softmax of (float32 logits)/temperature with log-sum-exp stabilization, then float rounding. Temperature is mm_temperature when any image is present, else temperature. Note the model-card snippet uses torch float32 softmax — a deliberate deviation to record, not adopt. |
| Undecided | The server returns probabilities for every candidate plus confidence = max(probabilities) and does not emit an undecided field; the card's threshold rule (top probability below threshold ⇒ undecided) is caller guidance read from decision_config.json. First slice preserves the reference wire shape; surfacing an explicit undecided flag is a review question, not a silent addition. |
| Responses | answers keyed by question ID in request order: choice adds choice (first max on ties), noul adds noul = probabilities["yes"], score adds expected_value = Σ level_index × probability. usage.input_tokens sums full per-question prompt lengths (image tokens counted per question), output_tokens is 0, plus a measured latency. Response model is the adapter name (JEMM). Zero generated tokens everywhere. |
| Reference extras | contract.py also contains an unexposed-over-HTTP bundle path (2–8 questions in one prompt, ≤4096 tokens, per-question readout at Answer qN: marker positions). Out of scope for the first slice; see §5. |
5. Execution and optimization plan
The reference re-encodes the full state+images prompt per question. The
obvious native win is encoding the multimodal prefix once per request and
running question suffixes against it — exactly the prefix-continuation work in
flight (#97, #99, and jev-vl #96's prepared image embeddings + language-prefix
state). The reference's own bundle mode is a third formulation of the same
idea.
Proposed sequence:
- Contract/export: pinned environment, artifact inventory, CPU fixtures
for every boundary in §4, documented wire input/output. - Native eager worker: full text+vision execution, one forward per
question, serial admission, frontend integration, parity vs the pinned
reference server. - Request-local prefix reuse: shared multimodal prefix across questions of
one request, coordinated with jev_vl: add native inference and prefix caching #96/qwen3_5: add prefix continuation operations #97/qwen3_5: run prompts that share prefixes once per prefix #99/decider: reuse request prefixes with conservative auto selection #113; the bundle readout is an
alternative formulation to evaluate with fixtures against the reference's
encode_bundle. - Further optimization: CUDA Graphs / packed GEMMs where profiling
supports them, coordinated with perf(open-jev): pack candidate projections within requests #102/cua_s1: add native multimodal language CUDA Graph replay #103/cua_s1: add native vision CUDA Graph replay #108/feat(decider): independent CUDA Graph replay mode #112.
Each stage is a separate hypothesis measured against its own baseline; no
combined speedup claims.
6. Validation and acceptance
- Freeze code/artifact hashes, dependency versions, dtypes, and numerical
gates before GPU work. Suggested initial gates (proposals, not results):
max absolute per-candidate probability drift 0.01 vs the pinned reference
server; matching decisions when the reference top-two margin exceeds
0.02; report near-threshold confidence crossings explicitly. - CPU fixtures (exact, from the pinned reference): rendered prompts and
token IDs for all three question types, default type, criteria ordering,
tool-spec rendering, whitespace-collapse vsstrip()distinction, compact
JSON state, Unicode and special-token text, 32 label token IDs, image
decode/pixel-cap/format errors, budget and body-size rejections, response
finishing including ties andexpected_value, usage accounting, and
malformed inputs. All tests/helpers/fixtures under roottests/jemm/. - Reserved-GPU parity vs the pinned reference server on the same
checkpoint: text and image requests, every question type, staged comparisons
(merged-export vs unmerged-PEFT; native vision outputs vs processor
outputs; final probabilities and decisions). - Direct-worker and frontend-proxied HTTP checks, including the first
request after readiness and health behavior. Revalidate Cua-S1, Open-Jev-27B,
and Open-Jev-9B consumers when shared code or the CUDA ABI changes. - Performance runs under the benchmark protocol
with a JEMM-aware response validator (32-candidate probability sums; bench: accept wire-rounded probability sums per label count #75's
per-label-count rounding tolerance already covers this shape). Separate
preparation, startup, first inference, warm latency, and throughput; retain
raw results. - Update
recipe/jemm/,src/models/jemm/README.md,
docs/supported-models.md, root README,mkdocs.yml, and tracker [Tracking]: Model support status #83 when
validated support lands. Record tested GPUs and scope; no general claims
from compilation or narrow fixtures.
Alternatives considered
- External Python worker first: the reference server already exposes
/v1/systemoneand/health, so it can run behind the existing frontend
(OMNI_JEV_BACKEND_URL) with zero code, like the LAYA external-worker
recipe. Useful as an immediate recipe and oracle; it does not meet the
native-serving goal and its throughput is bounded by the per-question
re-encoding. - Extending the Open-Jev worker: rejected; different prompt compilation,
readout, calibration, and modality would fork that model's validated
contract. - Adopting the bundle readout first: higher ceiling per request, but it
front-loads the hardest readout semantics before basic parity exists.
Community help wanted
| Contribution | A useful first deliverable |
|---|---|
| Upstream expertise (@MaestroYan, @ypcypc) | Confirm the base-model revision and environment, multi-image rendering intent, and the card-vs-server softmax difference; provide small redistributable image/question fixtures. |
| Contract and fixtures | Port the pinned request compiler and response finishing to Rust with exact CPU fixtures; CPU-only work is useful. |
| Export and checkpoint prep | Extend the merge/export path to JEMM's GDN-targeting adapter and vision weights; qualify BF16 merge drift. |
| Rust/CUDA integration | Wire the shared prefill + vision path for the 27B shapes; label-row projection; coordinate the prefix API with #96/#97/#99. |
| Independent validation | Reproduce parity and serving measurements on available NVIDIA hardware (≥64 GB). |
Comment with the task you want, relevant experience, and available hardware.
Small PRs and design review are welcome; nobody needs to own the whole
integration.
Review questions:
- Is text+vision in the first native slice right, or text-first with vision
second (given the shared vision entry points and Cua-S1 precedent)? - Server (float64) or card (torch float32) softmax as the normative
calibration reference? - Should the worker surface an explicit undecided signal from
threshold, or
preserve the reference server's probabilities+confidence shape exactly? - Which prefix-reuse formulation should JEMM adopt — question suffixes on a
shared multimodal prefix (jev_vl: add native inference and prefix caching #96/qwen3_5: add prefix continuation operations #97/qwen3_5: run prompts that share prefixes once per prefix #99) or the reference's bundle readout?
Feedback Period
Initial feedback through 2026-10-22; contributions and discussion remain
welcome afterward.
CC List
@MaestroYan — the model author; confirmation of the base pin, environment, and
training-time image assumptions would be especially helpful.
@ypcypc — the reference server author; contract clarifications (undecided
semantics, bundle plans) likewise.
Anything else
Preparation for this RFC inspected public model metadata and the pinned
reference source only. No model weights were loaded, no GPU inference or
benchmarks were run, and no native speedup is claimed.
JEMM is one of several Jev-family integrations now in flight; it is a distinct
checkpoint from the supported Open-Jev-27B/9B (ZefanCai), openjev/openjev
(#95), autotrust/JEV-27B-VL (#96), and OmniJev (#114), and is not affiliated
with TypeSafe AI.
License notes: adapter and reference server are Apache-2.0; the card's training
mix includes CC-BY-3.0/CC-BY-4.0 and OpenRAIL components (attribution only —
no weights are distributed here); confirm the base checkpoint's license at pin
time.
Related: #9 (roadmap), #61 (shared /v1/systemone contract), #83 (model
tracker), #85 (observation reuse), #95/#114 (sibling Jev-family RFCs), #96
(je-vl native + prefix caching), #97/#99 (prefix continuation/sharing), #102
(packed prefill, merged), #103/#108 (native multimodal graphs).
Before submitting a new issue
- Searched existing issues and PRs for
MaestroYan/JEMM,JEMM, and the
checkpoint ID; reviewed the RFC template, architecture contracts, supported
-model documentation, current native code, and the pinned upstream reference.
No existing JEMM integration was found.
Activity
Levius-Fubuki commentedon Oct 8, 2026
Draft PRs #117 and #118 add native JEMM support, with initial A800 parity and performance results in #118. Next: align error responses and calibration loading, add fixtures/docs, and coordinate Graph support with #103/#108.🫡