System One: Kev and Jev
The built-in systemone backend speaks the System One decision wire format: an input (state), typed questions, and typed answers with probabilities. One adapter covers every service that uses it:
- Kev — a Kev server you run yourself;
- Jev through OpenRouter — TypeSafe's Jev via OpenRouter's Decisions API;
- TypeSafe — compatible TypeSafe System One endpoints, directly.
The adapter has been validated with real inference against a self-hosted Kev-0.8B server and against Jev (typesafe/jev-1.13) through OpenRouter, on Linux and macOS, for choice, noul and score decisions. See Real-backend validation.
The question
System One models answer a question about the input, so the question must be stated. DecGuard uses the decision's description, or the backend's instructions setting when it is set. One of the two is required.
decision:
name: support_team
type: choice
description: Which team should handle this customer support ticket?
options: [billing, shipping, technical]Put guidance about the options into the description (or instructions): for choice decisions, option names are sent without descriptions.
Jev through OpenRouter
backend:
provider: systemone
model: typesafe/jev-1.13
url: https://openrouter.ai/api/alpha/decisions
bearer_token_env: OPENROUTER_API_KEY
timeout_s: 60
max_retries: 2
evaluation:
probability_tolerance: 0.02 # Jev rounds probabilities to two decimalsThe only credential is an OpenRouter API key, and it belongs in the OPENROUTER_API_KEY environment variable — never in the contract:
export OPENROUTER_API_KEY=...
decguard test decguard.yaml- Pin a model version (
typesafe/jev-1.13), not a moving alias. Each result records the dated snapshot that actually answered asmodel_version(for exampletypesafe/jev-1.13-20260917), so a silent upstream change shows up in regression diffs. - Probability tolerance. Jev returns probabilities rounded to two decimals, so K labels can sum to 1 ± K × 0.005. Set
evaluation.probability_toleranceto cover it (0.02for up to four labels). The sum is still checked and never rescaled. - Cost. Every case is one billed request, as is every transformed case during fuzzing. Token usage and cost are recorded per result (see below).
Self-hosted Kev
Run a Kev server, then point the backend at its System One endpoint. Kev serves /v1/systemone for decisions and /v1/models for the loaded model, which works as a healthcheck:
backend:
provider: systemone
model: kev-latest
url: http://127.0.0.1:8009/v1/systemone
health_url: http://127.0.0.1:8009/v1/models
timeout_s: 300 # CPU inference; the first request also warms the model upThis is the flow DecGuard validates in CI, with upstream Kev at a pinned commit serving Kev-0.8B, the smallest current checkpoint (a LoRA adapter and pointer head on Qwen/Qwen3.5-0.8B-Base). Kev needs Python 3.12 or 3.13 and uv. It runs on CPU (fp32) without a GPU, and on MLX (bf16) on Apple Silicon. The first start downloads about 1.8 GB of weights.
git clone https://github.com/jaredpalmer/kev.git && cd kev
git checkout 09ff745d52a0f23954e3b0f5a608bf4c7c6aebb4
uv sync --locked --extra serve
uv run --locked --extra serve python -m kev.serve \
--run jaredpalmer/kev-0.8b@9a45d25eb2ab761841196625383fa1dff0e56c1e --port 8009Once curl -s 127.0.0.1:8009/v1/models answers, decguard test can use it. Kev rounds probabilities to four decimals, so the default tolerance is usually enough.
A local Kev server needs no credential. To require one, set KEV_API_KEY on the server and bearer_token_env (naming a variable that holds the same key) in the contract.
TypeSafe endpoints
A TypeSafe System One endpoint uses the same adapter with its own URL, model name and key variable:
backends:
typesafe:
provider: systemone
model: jev-1.13.0
url: https://api.typesafe.ai/v1/systemone
bearer_token_env: TYPESAFE_API_KEYThe real-backend validation covers Kev and Jev through OpenRouter; direct TypeSafe endpoints share the wire format but are not part of that validation.
Comparing backends
Declare several System One backends in one contract and run the same dataset through each, then diff them:
backend: # a local Kev server
provider: systemone
model: kev-latest
url: http://127.0.0.1:8009/v1/systemone
health_url: http://127.0.0.1:8009/v1/models
timeout_s: 300
backends:
jev: # Jev through OpenRouter
provider: systemone
model: typesafe/jev-1.13
url: https://openrouter.ai/api/alpha/decisions
bearer_token_env: OPENROUTER_API_KEY
timeout_s: 60
max_retries: 2
evaluation:
probability_tolerance: 0.02decguard test decguard.yaml -o kev.json
decguard test decguard.yaml --backend jev -o jev.json
decguard diff kev.json jev.json --contract decguard.yamlSettings
systemone accepts every http setting — URL checks, health_url, timeouts, retries, environment credentials, label_map — plus:
| Setting | Default | Meaning |
|---|---|---|
model | required | the model to ask; sent with every request |
instructions | decision.description | the question asked about every input |
Differences from http:
- retries (
max_retries) also cover HTTP 429 and 529, which TypeSafe and OpenRouter ask clients to retry, besides connection failures and 502–504; label_mapapplies tochoiceandscoredecisions only, since no labels are sent fornoul.
Wire format
Each case is sent as one question about the case input, keyed by the decision name:
{
"model": "typesafe/jev-1.13",
"state": "I was charged twice for order #4411.",
"questions": {
"support_team": {
"type": "choice",
"instructions": "Which team should handle this customer support ticket?",
"criteria": {"billing": null, "shipping": null, "technical": null}
}
}
}| Decision | Sent | Answer used |
|---|---|---|
choice | options as criteria keys, descriptions null, in the order shown | probabilities by option; choice must be the most probable option |
noul | no labels (a yes/no question) | noul is the probability of the first (positive) contract label; the second gets 1 − noul |
score | levels as the ordered criteria list, lowest first | probabilities by level index ("0" = lowest); legend, when present, must echo the levels sent |
During fuzzing, options and levels are sent in the order and form shown and mapped back to the contract labels. A noul question has no labels, so option_order and label_format send the same request for it.
Answers are checked, never repaired. The response must answer exactly this question with the same type; a missing or malformed answer is an invalid_response for that case. The provider's own confidence field measures something else, so DecGuard ignores it: its confidence is the selected label's probability, as for every backend.
What gets recorded
Per result, DecGuard records the model that answered as model_version and adds to metadata:
request_id;upstream_provider— OpenRouter'sproviderfield;usage—input_tokens,output_tokensandcost.
The report's backend section lists the served_models and upstream_providers seen during the run. API keys never appear in reports, errors or terminal output.
Real-backend validation
The DecGuard repository runs genuine inference through the systemone backend on three small contracts in tests/integration/real/ — one choice, one noul and one score decision. Nothing is mocked. These tests are opt-in: a default pytest run skips them, and once selected they fail rather than skip when the server, checkpoint or credential is missing.
Kev (-m real_kev), with a Kev server started as above:
DECGUARD_KEV_RUN=jaredpalmer/kev-0.8b@9a45d25eb2ab761841196625383fa1dff0e56c1e \
uv run pytest -m real_kevDECGUARD_KEV_RUN is optional: when set, the test fails unless the server's GET /v1/models reports that checkpoint.
Jev through OpenRouter (-m real_jev):
export OPENROUTER_API_KEY=...
uv run pytest -m real_jevThis makes three billed Jev calls (about $0.00004 in total at the time of writing) and one call with a deliberately invalid key, which OpenRouter rejects with HTTP 401 and does not bill. The tests check that neither key appears in any output, error or report.
GitHub Actions. The repository's manual Real backends workflow (.github/workflows/real-backends.yml) runs both groups on ubuntu-latest and macos-latest with Python 3.13. It is started from the Actions tab or by adding the real-backends label to a pull request, never on ordinary commits. Only the Jev test step receives the OPENROUTER_API_KEY secret.
Any other backend (-m external) runs decguard test on your own contract, which must use a non-mock backend and produce at least one valid decision:
DECGUARD_EXTERNAL_CONTRACT=/absolute/path/to/real-decguard.yaml \
uv run pytest -m external