Skip to main content

jigor — System One decision gateway (Rust lib)

System One decision models in Rust, one wire protocol across backends: von and laya run locally as ONNX (via ort); jev runs remote on OpenRouter's Decisions API.

Models

  • HF sevenreasons/von-onnx-fp16 (model.onnx 759M, tokenizer/tokenizer.json 3.5M)
  • HF Mattepiu/laya-onnx (laya.onnx fp32 — matches the python reference bit-for-bit; int8/laya_int8.onnx via LAYA_ONNX_FILE)
  • OpenRouter Decisions (typesafe/jev-1.13 today) — any model there speaking the same System One wire works; add it to known_providers() and nothing else changes.

Usage as lib

One dependency, one entry per backend — noul/choice/score questions in, typed answers out. Identical on von, laya and any OpenRouter model.

[dependencies]
jigor = { path = "../jigor" }                             # local checkout
# jigor = { git = "https://github.com/Partysun/jigor" }   # from GitHub
# jigor = "0.1.0"                                         # once published
serde_json = { version = "1.0" }                          # Value, json!
use jigor::{Answer, Question, Backend, Result, VonBackend, choice, noul, score};
use serde_json::json;

fn main() -> Result<()> {
    let mut von = VonBackend::new()?;   // VonBackend::new, LayaBackend::new,
                                        // OpenRouterBackend::for_model(...):
                                        // the same answers() call on all three
    let questions = vec![
        noul("churn", "Is the customer likely to churn?"),
        choice(
            "want",
            "What does the customer want?",
            &["refund", "order status", "technical help"],
        ),
        score("urgency", "How urgent is this?", &["calm", "annoyed", "angry"]),
    ];
    let asks = von.answers(
        &json!("Customer: I was charged twice for order #4471."),
        &questions,
        None,
    )?;

    match asks.get("churn").unwrap() {
        Answer::Noul { probability } => println!("churn: {:.4}", *probability),
        _ => {},
    }
    Ok(())
}

Only have a model id? jigor::ask resolves aliases and the provider for you:

let asks = jigor::ask("jev", &state, &questions, None)?;  // OpenRouter jev
let asks = jigor::ask("laya", &state, &questions, None)?; // local laya
// Asks { model, backend, answers }

Errors are one type — jigor::Error (carried by jigor::Result<T>): UnknownModel/MissingApiKey/Remote for routing and OpenRouter responses, Wire/MissingAnswer/MissingAnswers for malformed question/answer payloads, Serialization for JSON text, External/Internal for everything else. No foreign error type ever leaks out of the library.

use jigor::{Error, Result};

match von.answers(&state, &questions, None) {
    Ok(asks) => { /* typed answers */ }
    Err(Error::Remote { status, message }) => { /* upstream 4xx/5xx */ }
    Err(e) => println!("{e}"),
}

Production: library vs installed CLI/server

One workspace, two crates (crates/jigor + crates/jigor-cli), one pip distribution — the same code, three ways to consume it:

  • Library — jigor = { version = "0.1.0" } in your crate (see Usage as lib). Only the library target is compiled: the CLI/server code and its dependencies (hyper, tokio, ...) never enter consumer builds.
  • CLI + server (cargo) — cargo install jigor-cli installs the jigor binary: jigor serve, jigor ask, jigor models.
  • CLI + server (npm) — npm install --global jigor (prebuilt binary, per-platform packages: linux x64/arm64, macos universal2 — Intel + Apple Silicon, windows x64).
  • CLI + server (pip) — pip install jigor (maturin wheel) installs the same jigor console script.

The local ONNX models (von, laya) download to ~/.cache/huggingface on first use; set OPENROUTER_API_KEY for the remote jev backend. Both crates are published together (make publish) from the shared version in Cargo.toml; the wheel and the npm binary packages are published by the release pipelines (.woodpecker/release-*.yml, .woodpecker/npm.yml).

Run examples (lib crate, no bin)

cargo run -p jigor --example decide            # same as Python main.py
cargo run -p jigor --release --example bench   # bench
cargo run -p jigor --example decide --offline

Expected decide:

infrastructure
0.428
{'infrastructure': 0.6203, 'billing': 0.1873, 'feature_request': 0.1924}
judge: 0.3586
rate score: 1.02 conf: 0.707 probs: {"1": 0.8109, "2": 0.1036, "0": 0.0855}
fan-out intent: payment_failure 0.539

Set JIGOR_DEVICE=cuda to try CUDA EP (ort cuda feature, fallback to CPU if unavailable).

Run as executable: jigor

cargo build -p jigor-cli --release
./target/release/jigor serve --host 0.0.0.0 --port 8000   # HTTP gateway
jigor models                                              # provider x model pairs

The gateway mirrors the library: noul/choice/score questions in, typed answers out — von/laya locally, anything else routed to the OpenRouter backend selected by the model field:

jigor ask <<'JSON'   # same wire over stdin, no HTTP layer
{
  "state": { "error": "Disk volume /var/log at 98% capacity." },
  "questions": {
    "requires_intervention": {
      "type": "noul",
      "instructions": "Does this disk space condition require operational intervention?"
    }
  }
}
JSON
# {"model":"von-1.0.0","backend":"local","answers":{...}}

# pin the provider, or use the "jev" alias for typesafe/jev-1.13 on OpenRouter:
jigor ask --provider openrouter --model jev < request.json
jigor ask --model jev < request.json          # provider inferred from the id
curl -X POST http://localhost:8000/v1/systemone \
  -H "Content-Type: application/json" \
  -d '{
    "model": "von-1.0.0",
    "state": { "error": "Disk volume /var/log at 98% capacity." },
    "questions": {
      "requires_intervention": {
        "type": "noul",
        "instructions": "Does this disk space condition require operational intervention?"
      }
    }
  }'
# {"model":"von-1.0.0","backend":"local","answers":{"requires_intervention":{"type":"noul","noul":0.2739}}}

Route by provider + model pair — typesafe/jev-1.13 (alias jev) goes to OpenRouter, von-1.0.0 (alias von) stays local; unknown pairs are rejected.

Also: GET /healthz returns {"status":"ok"}.

Tweet Tester (example: using the lib)

examples/tweet.rs is a complete Tweet Tester written only against the lib API — it demonstrates how to build a Jev-style tool on top of noul/choice/score questions through one answers interface. All tweet-specific code lives in the example (the lib itself stays generic):

  • the 61-question viral-score bank (question set v1.1, 8 families EMO/CNV/SHR/TIM/CRF/IDN/FMT/ANTI);
  • a transparent 0-100 aggregation (0.65 * content mean + 0.35 * clean anti-signal) — scoring semantics: 50 = your account's normal post, above 50 beats it, below 50 does worse;
  • per-family "fired % of N questions" radar stats, top helped/hurt, and engagement counters (a wire JSON shape mirroring the viral-score API).
cargo run -p jigor --example tweet -- "We just crossed 10,000 paying customers. Thank you."
cargo run -p jigor --example tweet -- --json "We just crossed 10,000 paying customers."
# {"score":53,"beats_own_normal":0.53,
#  "families":{"EMOTION":{"label":"Emotion","fired":0.11,"total":9},...},
#  "counters":{"likes":{"multiple":1.1,"p75":2.1,"p90":4.6,"breakout_share":0.1,"probability":0.5,"confidence":"normal","own_median":null,"expected":null},...},
#  "helped":[{"id":"k_concrete_numbers","family":"CRAFT","label":"Numbers that carry weight","answer":"Yes","detail":"Stronger than your usual post","effect":0.95}],
#  "hurt":[...],"answers":[...61 items...],"engine":{"model":"von-1.0.0","question_set":"v1.1",...}}

The example resolves the backend by model, exactly like jigor ask: --model von (default, local ONNX), --model jev (OpenRouter) or any other alias. The 61-question bank, score, radar and counters are identical across backends, so you can compare the same tweet side by side:

cargo run -p jigor --example tweet -- --model von "We just crossed 10,000 paying customers. Thank you."
cargo run -p jigor --example tweet -- --model jev "We just crossed 10,000 paying customers. Thank you."
cargo run -p jigor --example tweet -- --model jev --json "Hot take." > jev.json

For the milestone tweet the outputs line up: von gives 53/100 (conservative, CRAFT 35%), jev gives 63/100 (CRAFT 73%). Scores across backends are not calibrated to each other, but the shape is directly comparable.

Family fired is the "X% of its questions fired" radar value. The engagement counters (multiples, p75/p90, probabilities) and the effect coefficients are transparent placeholders for a fitted engagement model — tune the weights in the example's counter_json/counter_multiple once you have paired data. Von is a general decision model, so scores are a signal, not a forecast; fitting an engagement model on the same answers is the calibration step.

Question builders: choice takes plain option strings (each is both key and description), choice_pairs takes (key, description) pairs, score takes ordered level texts, noul a plain instruction — and all three kinds mix freely in one answers call.

Auto-tagger (example: using the lib)

examples/tagger.rs shows a choice workflow: given a note and a list of existing tags, one question — "Which tag best matches the content of this note?" — picks the best tag (or the None of these fit well fallback) with a probability distribution. Also runs on either backend via --model/--provider.

cargo run -p jigor --example tagger -- --title "Hiring notes" --tags "work, ideas, personal" "Budget approved for two engineers."
# Best tag: work  (confidence 0.440)
#   work 60%  ·  ideas 16%  ·  personal 13%  ·  None of these fit well 10%
cargo run -p jigor --example tagger -- --model jev --tags "bugs, docs, ship" "Fixed the retry loop that dropped webhook events."
cargo run -p jigor --example tagger -- --json --tags "a, b" "note text"   # wire JSON out

Integration tests

Requires hurl and the local ONNX model (downloaded on first run). Wire fixtures live in tests/fixtures/ (the OpenRouter Decisions request/response payloads are the reference for the wire format).

make test        # unit tests + hurl suite + CLI tests
bash tests/hurl/run.sh   # jigor serve: /v1/systemone (health, noul, choice,
                         # score, fan-out, error paths, backend routing)
bash tests/cli/ask.sh    # jigor ask / jigor models over stdin fixtures
# or against a running server:
hurl --test --variable BASE_URL=http://localhost:8000 tests/hurl/*.hurl

Release files for jigor 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for jigor 0.1.0
File Size Uploaded
jigor-0.1.0.tar.gz 76.3 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for jigor 0.1.0
File Interpreter ABI Platform
jigor-0.1.0-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details
jigor-0.1.0-py3-none-macosx_11_0_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl Python 3 none macOS 11.0+ ARM64, macOS 11.0+ x86-64, macOS 11.0+ universal2 (ARM64, x86-64) Details

Total release size: 25.5 MB

Release files / jigor-0.1.0.tar.gz

Download URL jigor-0.1.0.tar.gz
Size 76.3 kB
Tags Source
SHA-256 checksum
How to use checksums
7af7e33162cfa1f791d6295f94e15a7624db1b72ea49e37c524dd69c60f6fc80
BLAKE2b-256 checksum
How to use checksums
c739a9498fb308e95aa9e96364b54802dd6cb73cf447c11d43f544bd99ac47e9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"13","id":"trixie","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / jigor-0.1.0-py3-none-win_amd64.whl

Download URL jigor-0.1.0-py3-none-win_amd64.whl
Size 9.0 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
4ab5cb0d2273c62aedaf78578ca7ad1216662e34cd37831fc89c669ec17b08c2
BLAKE2b-256 checksum
How to use checksums
25a9fa1737138123ceb4074af2d05eca266d873391aac53e3af72d5f006061da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / jigor-0.1.0-py3-none-macosx_11_0_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl

Download URL jigor-0.1.0-py3-none-macosx_11_0_x86_64.macosx_11_0_arm64.macosx_11_0_universal2.whl
Size 16.5 MB
Tags Python 3 macOS 11.0+ ARM64 macOS 11.0+ universal2 (ARM64, x86-64) macOS 11.0+ x86-64
SHA-256 checksum
How to use checksums
950ebe7526737d0fa982eb9c93dfa0594299f2121b720713551917d58746eba4
BLAKE2b-256 checksum
How to use checksums
b8672c1d430e324b7935b03b55c791defa3fe7f88ed223e824ba05f9e2eb5dff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

0.1.1

3 release files

This release

0.1.0 This release

3 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page