basal-omni-pico

GitHub Website

basal-omni-pico is our smallest typed-decision model for Polish and English. It has 0.8B parameters, it is built on Qwen3.5-0.8B-Base (the larger basal-omni models are built on Gemma 4), it reads text only, and it was trained with our basal-omni-1.0-ultra (31B) as the teacher. It reads the same requests as the larger basal models.

What it is. The model reads a state (a message, a document, a sensor log, retrieved passages) and answers a typed question about it (choice, yes/no noul, score, multi, act, tool) by returning a probability for each allowed answer, in one forward pass, without generating text. The answer never falls outside the options you give.

model params base
basal-omni-pico 0.8B Qwen3.5-0.8B-Base, text only; also as GGUF for llama.cpp (basal-omni-pico-GGUF)
basal-omni-1.0-mini E4B Gemma 4 E4B
basal-omni-1.1-max 12B Gemma 4 12B
basal-omni-1.0-ultra 31B Gemma 4 31B

Intended use

  • Good for: routing a request to the right queue, team or tool, and picking the relevant passage (Werdykt: routing 0.912, retrieval 0.886, real documents 0.848); Polish decisions (our Polish check set: 0.876).
  • Languages: Polish and English.
  • Not for: writing text or chatting; facts that are not in the state; rule checks (Werdykt rules 0.316); using the probabilities as error rates on a new kind of data before you check them there (see Calibration); decisions with serious consequences for people without human review. For harder cases use a larger basal model.

Quick start

1. Install basal 2.4.0 into a fresh uv environment (CPU wheels shown; for an NVIDIA GPU use the cu128 index and add "flash-linear-attention==0.5.2", on a Mac the default index):

uv venv --python 3.12 --allow-existing ~/basal-env && source ~/basal-env/bin/activate
uv pip install torch==2.11.0 --index-url https://download.pytorch.org/whl/cpu
uv pip install "basal @ https://github.com/rkinas/basal/archive/refs/tags/v2.4.0.tar.gz"

2. Start the server. It prints basal: model ... ready:

basal-serve --model Remek/basal-omni-pico --port 8000

Without --mode, basal 2.4 serves this model in its fast CUDA-graph engine (fast) on Hopper GPUs (H100) when flash-linear-attention is installed, and in eager elsewhere. basal 2.4 or later is needed for --mode fast; 2.3.2 or later serves it in eager and gguf. fp8 and nvfp4 refuse this model.

3. Ask questions, as with every basal model:

curl -s localhost:8000/v1/systemone -H 'content-type: application/json' -d '{
  "state": "Klient: od wczoraj nie mogę zalogować się do bankowości internetowej, system pokazuje błąd hasła.",
  "questions": {
    "dept":   {"type": "choice", "instructions": "Do którego działu skierować zgłoszenie?",
               "criteria": {"cards": "Reklamacje kart", "online": "Wsparcie bankowości elektronicznej", "loans": "Kredyty"}},
    "urgent": {"type": "noul", "instructions": "Czy klient nie może korzystać z usługi?"}}}'

The answer (this model on one H100, installed as above, shortened):

{"model": "basal-omni-pico",
 "answers": {
  "dept":   {"type": "choice", "choice": "online", "probabilities": {"cards": 0.0009, "online": 0.9983, "loans": 0.0008},
             "confidence": 0.9983},
  "urgent": {"type": "noul", "noul": 0.762, "probabilities": {"true": 0.762, "false": 0.238}, "confidence": 0.762}}}

Full API (multi, act, tool, option keys, max_error): API.

GGUF (llama.cpp, CPU or GPU)

basal-omni-pico-GGUF has a Q8_0 file with its own calibration file, served by the same basal-serve with --mode gguf (llama.cpp in process, basal[gguf], basal 2.3.2 or later). On 1,700 development items it picks the same answer as this model in 99.24% of cases on one H100 and in 99.29% on the CPU. On 16 CPU threads the request above takes about 0.5 s.

Results

Werdykt (our hidden Polish + English benchmark, 5,000 items): macro 0.660 [0.648, 0.671] (Polish 0.661, English 0.659). For comparison, basal-2.0-mini (1.5B) scores 0.618, basal-2.0 (4.5B) 0.729 and basal-omni-1.0-mini 0.769. By category: routing 0.912, retrieval 0.886, real documents 0.848, judging 0.752, scores 0.624, actions 0.592, long states 0.582, abstaining 0.544, contracts 0.542, rules 0.316.

Against the other decision models of 2B parameters or less that we ran (the same benchmark client for every model, each served by its own runtime):

model params Werdykt macro [95% CI] Polish / English Werdykt ECE dev v2 LEK PL / EN Polish check
basal-omni-pico 0.8B 0.660 [0.648, 0.671] 0.661 / 0.659 0.138 0.646 35.9 / 45.1 0.876
decider-2b (Mapika) 1.9B 0.587 [0.574, 0.599] 0.580 / 0.593 0.123 0.576 45.0 / 59.1 0.582
krino-2b 2B 0.566 [0.553, 0.579] 0.552 / 0.580 0.051 0.528 36.8 / 50.7 0.598
decision-2b (flymy-ai) 2B 0.546 [0.534, 0.558] 0.492 / 0.600 0.030 0.532 29.0 / 45.7 0.542
qev-0.8b 0.8B 0.544 [0.530, 0.556] 0.530 / 0.558 0.088 0.572 33.2 / 44.5 0.592
jiwo-0.8b 0.75B 0.542 [0.529, 0.554] 0.516 / 0.567 0.047 0.518 30.0 / 40.8 0.533
  • dev v2: our development set of 10,606 decisions (accuracy). LEK: the Polish medical final exam, Polish and English versions. Polish check: our Polish decision check set.
  • Where others are ahead: decider-2b on LEK (45.0 / 59.1) and on our English, generated-Polish and panel check sets; every other model in the table is better calibrated on Werdykt (ECE 0.030 to 0.123 against 0.138), and decider-2b automates 9.8% of Werdykt at a 1% target, where this model automates none.

Public benchmarks: BH 75.3 (ECE 0.070); JevExtra 68.4, JevSpatial 68.7, Rules-7 68.6; JevBench accuracy 83.9, coherence 74.3. qev-0.8b is ahead on BH (77.9), JevExtra (71.8), JevSpatial (74.2) and JevBench accuracy (85.1).

Development sets (not test sets): dev macro 0.685 (panel 0.789, BH 0.552, JDI-shaped 0.706, agent 0.693); dev v2 accuracy 0.646 (Polish 0.633, English 0.656); our check sets: Polish 0.876, generated Polish 0.601, English 0.607, real Polish cases 0.940, panel 0.593.

Calibration

CALIBRATION.json holds per-type temperatures and certified thresholds for max_error 1% and 5%. It was fitted on readouts of this model as basal-serve runs it (--mode eager, bf16, one option order, the default), on our calibration set (fit and certification splits), and evaluated on a frozen test split of 8,560 decisions:

choice yes/no (noul) score all
ECE after the temperature (test) 0.0125 0.0109 0.0645 0.0059
accuracy (test) 0.883 0.813 0.601 0.831
  • Automation on the test split: at max_error 1%, 35.0% of decisions are decided automatically (observed error 0.93%); at 5%, 58.2% (observed error 4.38%). At 1% only choice questions have a threshold (yes/no and score questions defer); at 5% score questions defer.
  • Held-out-family check (each task family left out of the fit in turn, pooled ECE per type): choice 0.015, yes/no 0.016, score 0.038, all under our 0.05 bar.
  • CALIBRATION.o2.json is the same fit for --orders 2 (both option orders); basal-serve picks it when you serve with --orders 2.
  • What the guarantees cover. The 1% and 5% thresholds hold on our held-out test split (35.0% and 58.2% of decisions automated, above). On Werdykt, an out-of-domain benchmark, served with this file, the model is overconfident: mean confidence 0.80 against accuracy 0.66 (ECE 0.138, Brier 0.475). At a 1% target nothing is automated; at 5%, 33.9% is automated with an observed error of 6.3%, so the 5% target is not met there.
  • Outside our domains, refit first. Fit the thresholds on your own labelled cases (basal-calibrate) before you automate with max_error.

Speed

basal 2.4 on one H100 80 GB, basal-serve --no-calibration, one connection per request; p50 at concurrency 1 / throughput at 16 clients (decisions/s; questions/s with five questions per request). Short states are about 1,500 tokens, long ones 3,000–10,000.

path 1 question, short state 5 questions, short state 1 question, long state 5 questions, long state
--mode fast (basal 2.4, the default on an H100) 13.5 ms / 134.6 26.6 ms / 400.4 45.9 ms / 34.4 90.6 ms / 94.4
--mode fast-nocompile (basal 2.4) 17.4 ms / 89.3 33.5 ms / 328.2 64.0 ms / 22.9 110.5 ms / 80.1
--mode eager 45.5 ms / 32.8 112 ms / 41.9 183 ms / 6.6 908 ms / 6.6
--mode vllm (vLLM 0.31.0) 54.3 ms / 93.9 67 ms / 188.7 39 ms / 43.7 109 ms / 90.6
--mode gguf Q8_0 on the GPU 56.5 ms / 18.1 98 ms / 54.7 283 ms / 4.7 360 ms / 18.6
  • basal 2.4 serves this model in its fast CUDA-graph engine: 3.5x eager's speed on one question and 4-10x with five questions over one state, whose recurrent state is computed once for all of them. Install flash-linear-attention for it: uv pip install "flash-linear-attention==0.5.2". Without it, and on GPUs other than Hopper, the model runs in eager unless you pass --mode fast (on other GPUs it has not been checked).
  • The same answers as eager: in fp32 the top answer agrees with --mode eager on 99.94–99.99% of 1,700 development items (one question, five questions, two option orders, both); in bf16 fast agrees with the fp32 answers as often as eager does (99.44–99.65% against 99.48–99.71%). Under load, --deterministic and batched serving gave the same top answer on 99.83% of the 10,606 dev decisions (eager: 99.42%).
  • One question over a long state is still faster in --mode vllm (39 against 46 ms).
  • Werdykt's own speed pass on this model (one H100, eager): 47 ms p50 at concurrency 1, 32.4 decisions/s at 16.
  • --mode vllm agreed with eager on the top answer for 99.47% of 1,700 development items (same checkpoint); it has no calibration file of its own and uses CALIBRATION.json. --mode fast uses CALIBRATION.json too.
  • CPU: see the GGUF section.

Evaluation notes

  • Contamination checks on the training mix (exact, 13-gram and short-state matching), hits left in the final mix in brackets: Werdykt, hidden (0); the Jev Decision Index 0.3 test requests, 150 rows dropped (0); our evaluation documents, 204 rows dropped (0); the insurance benchmark v1 325 + v2 3,042 items (0); LEK, the Polish medical final exam, pl 4,727 + en 3,151 items, 331 rows dropped (0): statute and social-insurance wording that our legal rows share with 29 medical-law questions, not exam items.
  • Werdykt (hidden benchmark). Run on these weights with basal 2.3.1 --mode eager (flash-linear-attention 0.5.2), one H100 80 GB (RunPod), 5,000 of 5,000 items answered; then rerun on a RunPod pod with this repository's files (basal 2.3.2, --mode eager, the shipped CALIBRATION.json): the same macro (0.660). The Werdykt calibration numbers on this card come from the rerun.
  • Selection. Two runs of the same recipe (seeds 0 and 1) and their 50/50 weight average were the candidates. The rule fixed before the runs picked the single seed-0 run, since the average led it on dev by only 0.28 points (0.685 vs 0.682, inside the rule's 0.5-point tie). We release the average: we chose it after both Werdykt results were known (0.660 vs 0.649, overlapping intervals).

Files

  • model.safetensors, config.json, generation_config.json: the weights (bf16; the 18 A_log tensors and the 18 Gated DeltaNet norms in fp32; Qwen3_5ForCausalLM, text only); model.safetensors sha256 ac52f85bf4af603b2d2408d314e9c1bb59e351884f7cacd72464c49dca9149e9
  • the tokenizer files and chat_template.jinja
  • basal.json (prompt format 2, readout), CALIBRATION.json (one option order), CALIBRATION.o2.json (two)
  • MANIFEST.md: training data by source and language, guard reports
  • SHA256SUMS: the sha256 of every file above (check with sha256sum -c SHA256SUMS)

How it works

Prompt format 2 of the basal engine: the state and the question with its options as one prompt, then a short answer prefix; the logits of the option codes at the first answer position, scaled by the calibrated temperature of the question type, are the probabilities. Nothing is generated.

Training

Full fine-tuning of Qwen/Qwen3.5-0.8B-Base (revision dc7cdfe2ee4154fa7e30f5b51ca41bfa40174e68; the language model, every weight) on 1,428,543 rows in prompt format 2, none over 6,144 tokens: the guarded basal training mix plus 100k generated short edge cases (rows by source and language: MANIFEST.md). Two runs with seeds 0 and 1, each 1.4 epochs: 27,778 steps at a global batch of 72 (8 sequences × 3 accumulation steps × 3 GPUs), learning rate 5e-6 with 20 warm-up steps and a cosine decay to 0.1×, AdamW (0.9, 0.95), no weight decay, fp32 master weights with a bf16 forward. The released weights are the 50/50 average of the two final checkpoints.

The loss is cross-entropy on the gold answer plus three terms:

  • a Brier term on choice questions (weight 0.5);
  • the squared error of the expected level on score questions (weight 1.0);
  • distillation from basal-omni-1.0-ultra (31B), whose option-code probabilities are the targets (weight 1.0) on 1,254,097 rows. It is skipped on 67,885 rows where the teacher's top answer differs from the gold label, and on yes/no rows where the teacher is not decisive.

Trained on 3× H100 with PyTorch 2.11, the flash-linear-attention 0.5.2 kernels for the Gated DeltaNet layers and Triton 3.7.1.

Limitations

  • Trained on prompts of up to 6,144 tokens (Werdykt long states: 0.582); at most 255 options per question (prompt format 2).
  • A 0.8B model knows little: put every fact the decision needs into the state.
  • Rule checks are its weakest area (Werdykt rules 0.316): check it on your own rule cases before you rely on it.
  • Exact arithmetic is a weak spot of one-pass decisions: compute numbers in code and pass the result.
  • Overconfident on new kinds of data (see Calibration).
  • Decisions with serious consequences for people should be reviewed by a person.

Citation

@software{kinas2026basal20,
  title   = {basal-2.0: typed-decision models and inference engine for Polish and English},
  author  = {Kinas, Remigiusz},
  year    = {2026},
  version = {2.0.0},
  url     = {https://github.com/rkinas/basal}
}

License and attribution

Apache-2.0. Fine-tuned from Qwen/Qwen3.5-0.8B-Base (Apache-2.0) by the Qwen team; the base's vision encoder is not included (text only).

Downloads last month
18
Safetensors
Model size
0.8B params
Tensor type
F32
·
BF16
·
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support

Model tree for Remek/basal-omni-pico

Finetuned
(149)
this model
Quantizations
2 models

Space using Remek/basal-omni-pico 1