DOCUMENTATION
Make your model OpenDXP compatible
A ten-minute guide: package a System One model so any engine runs it without your code, prove it with a conformance file, and publish it to earn the OpenDXP compatible badge.
View as Markdown · for AI agents: llms.txt · skill.md
Your model already answers the System One request: a state, typed questions, a
calibrated probability for every option. Packaged for
OpenDXP, it also runs without your code: on a laptop with
opendxp run, behind an HTTP API with opendxp serve, as a tool for AI
agents with opendxp mcp, and in the System One Engine behind the live
playgrounds here. This guide takes about ten minutes to read; packaging a model
takes an afternoon. Publish the package with your model, and once it passes its
check, your model and your profile show OpenDXP compatible.
What you will have
- A folder,
odxp/, with anodxp.jsonmanifest naming every file and its SHA-256: the weights, the tokenizer, the input layout as data, the calibration, and a conformance file. - A conformance file written by your own code: what your model answers on a fixed set of requests.
- A check report from the reference runtime. When it passes, your model is OpenDXP compatible, and its page here says so.
1. Pick the profile
| Your model | Profile | Weights | Input described in |
|---|---|---|---|
| A bidirectional encoder that scores a marker token per option (Laya, Julia 1) | encoder-markers | ONNX | template.json |
| A language model that reads each option letter's probability at an answer slot (Decider) | causal-letters | GGUF | prompt.json |
| An instruction-tuned model asked in its chat template, option by option in rotation (AnyJev) | causal-letters, typed layouts (0.2) | GGUF | prompt.json |
If your model fits none of these, it can still be listed on System One Models and run with its own code. Tell us how it reads its answer: that is what the next version of the standard needs to know.
2. Export the weights
ONNX (encoder-markers). The graph takes input_ids, attention_mask,
marker_positions, marker_mask and question_type, returns
option_logits, and names its dynamic axes batch, tokens and
options; opset 17 or later. opendxp.export.onnx_graph.export_graph does
this for any torch module with that forward, and points the graph at your
model.safetensors as external data instead of copying it: the package adds
a few megabytes, and its weights hash is the one you published.
GGUF (causal-letters). Your GGUF file, unchanged. If you publish only
safetensors, convert them at the precision your own code runs, with llama.cpp's
convert_hf_to_gguf.py (F16 for BF16 weights). A quantised file such as
Q8_0 is a derived package: it is checked against the answers of the original
weights, and on a small model it usually changes a few decisions.
3. Describe the input as data
This is the heart of it. Nothing in a package is executed: no Python, no Jinja.
Templates are strings with placeholders such as {instructions},
{state}, {label} and {text}, filled in one pass, so text from a
request is never expanded again.
template.json(encoder-markers): the special tokens, how the head and each option are written, the token budgets, and what happens when a state is too long.prompt.json(causal-letters): the context and question pieces, the option labels (each a single token), the layouts by option count, how score questions are asked, and the llama.cpp settings that change the numbers (flash attention, the cache type). A typed prompt adds the chat template, as the text it renders to with{system}and{user}in place, and the rotation rule.
A noul question, written the way AnyJev asks it:
"noul": {"layouts": [{"max_options": 2, "labels": ["No", "Yes"],
"head": "Question: {instructions}{legend}\nAnswer ",
"option": "{label}", "separator": " or ", "tail": "."}],
"labels": "attached", "rotate": "cyclic", "listing": ["true", "false"]}
Test the description the way the reference tests do: build a few hundred random requests, and require your template to give the same token ids as your own prompt builder, row for row.
4. Declare the calibration
calibration.json holds the temperature your code applies, per question type
and optionally per number of options. Write the value your code applies: if
it clamps a trained temperature into a range, write the clamped value, and keep
the raw one under source.
5. Record your model's own answers
A native adapter is a small function that loads your model with your own package and answers a System One request. With it, the conformance generator runs the standard request set (52 requests, 91 questions, 11 languages, 2 to 20 options, a 6,687-character state) through your code on a CPU:
opendxp conformance generate odxp/ --native path/to/checkpoint --runtime yourmodel
The file records every probability, and every request your model refuses: an engine must refuse it too.
6. Check it
opendxp validate odxp/ # schemas, parsers, coverage, hashes
opendxp check odxp/ # replays conformance.jsonl through the reference runtime
The check passes when every question gets the same decision and every probability is within 0.01 of your own code's. When it does not, the cause is almost always one of four things:
- Tokenization at a boundary. Encoding two pieces separately gives different ids from encoding them as one string. Describe which one your code does.
- Backend settings. On llama.cpp, switching flash attention off moved Decider's probabilities by up to 0.014; AnyJev passes only with an f32 cache. Declare the settings your own engine uses.
- Calibration. A clamp, or a temperature per option count, you forgot to declare.
- Quantization. A smaller weights file is a derived package; check it, but do not expect it to pass.
7. Publish it
Put the package in an odxp/ folder next to your systemone.yaml and push
the version:
systemone push ./my-model --repo your-org/your-model --dry-run
systemone push ./my-model --repo your-org/your-model
The engine finds the package by its odxp.json and checks it against its
conformance file by itself (packages up to 2.5 GB; ask us for larger ones).
When it passes, OpenDXP compatible appears on the model's page and on your
profile or your organization's. For a live playground as well, open the
model's Playground tab and ask for one. Converters already exist for Laya, Julia 1, Decider and AnyJev
(opendxp export); for anything else, write to [email protected].
The badge
There is one OpenDXP badge, and it cannot be asked for: it is earned by publishing. When a model you publish into your namespace (your username or your organization) carries a package that passes its check, OpenDXP compatible appears on the model's page, on its card in every list, and on your profile or your organization's. If a later version's package fails its check, the model shows OpenDXP package instead, with what failed.