Naluri (XLM-R base, M6-pairs)
Instinctive, multilingual decisions in one pass.
Naluri (Indonesian for instinct; Neural Answering via Label Understanding and Rapid Inference) is a small System One decision model: about 280M parameters, built on XLM-RoBERTa base. You give it a text or JSON state and a typed question with its options written out. It returns calibrated probabilities over those options in a single forward pass, taking about 5 ms per question on a laptop GPU and about 50–60 ms on a CPU. It never generates text.
It supports the same three question types as TypeSafe Jev and Laya:
| Type | Options | Returns |
|---|---|---|
noul | yes / no (optional descriptions) | p(true) |
choice | 2+ named options (optional descriptions) | one probability per option |
score | ordered levels | one probability per level; expected level = score |
Code, training pipeline and full report: github.com/jerichosiahaya/naluri.
Versions:
| Revision | Model | Notes |
|---|---|---|
main | M6-pairs | recommended |
m7 (branch) | M7 | experimental: higher typed-decisions score (48.6%), but partly from shifted default answers |
m5, m4 (tags) | M5, M4 | earlier releases |
Load with the bundled
naluri.py(see Usage). The standardtransformerspipeline and the hosted inference widget can't run Naluri: its decision head is custom code.
Usage
pip install torch transformers safetensors huggingface_hub
from huggingface_hub import hf_hub_download
import importlib.util
# naluri.py (input format + model + loader) ships with this repo
spec = importlib.util.spec_from_file_location("naluri", hf_hub_download("jerichosiahaya/naluri-xlmr-base", "naluri.py"))
naluri = importlib.util.module_from_spec(spec); spec.loader.exec_module(naluri)
tok, model = naluri.from_pretrained("jerichosiahaya/naluri-xlmr-base", device="cpu") # or device="cuda"
state = {"order": {"id": "A-1029", "status": "delayed", "days_late": 6},
"customer": {"tier": "VIP"}, "note": "Asked twice already, threatening to cancel."}
questions = {
"action": {"type": "choice", "instructions": "What should support do next?",
"criteria": {"apologize": "send an apology only", "discount": "offer a discount voucher",
"escalate": "escalate to a supervisor now", "wait": "wait for the courier update"}},
"churn_risk": {"type": "noul", "instructions": "Is this customer likely to leave?"},
"mood": {"type": "score", "instructions": "How upset is the customer?", "criteria": ["calm", "annoyed", "angry"]},
}
print(naluri.predict(tok, model, state, questions, device="cpu"))
# {'action': {'apologize': ..., 'escalate': ...}, 'churn_risk': {'false': ..., 'true': ...}, 'mood': {0: ..., 1: ..., 2: ...}}
# a specific version, e.g. the experimental M7 branch:
from huggingface_hub import snapshot_download
tok, model = naluri.load(snapshot_download("jerichosiahaya/naluri-xlmr-base", revision="m7"), device="cpu")
- Option descriptions matter. Naluri reads them, so clear, distinct descriptions give better answers.
- Limits: keep each question under about 20 options, and the question plus options under about 192 tokens. Inputs are capped at 512 tokens in total.
- Uncertainty is meaningful: probabilities near 0.5 (or several options close together) are the cases to send to a human or an LLM.
Results
These numbers are for this checkpoint (M6-pairs, single seed). All models answered identical questions: Laya-multilingual was run zero-shot, and Jev was called live through its API. Unseen languages and unseen tasks are marked. More details are in the report.
| Eval | Naluri (this model) | Laya-multilingual (zero-shot) | Jev 1.13.0 (live) |
|---|---|---|---|
| SIB-200 topic, 12 languages (trained on other topic datasets) | 73.2% | 70.8% | – |
| XCOPA cause/effect, 11 languages (unseen task) | 57.2% | 54.7% | – |
| Belebele reading, 12 languages | 36.2% | 33.1% | – |
| Sentiment, new domain + languages | 61.1% | 52.7% | – |
| Intent (MASSIVE), 5 unseen languages | 75.8% | 37.6% | – |
| XNLI, 5 unseen languages | 58.1% | 46.1% | – |
| Review stars, 1–5 exact (MAE) | 55.7% (0.58) | 29.0% (1.45) | – |
| AG News topic | 74.1% | 92.0% | 88.0% |
| typed-decisions, zero-shot (2,000 business decisions) | 46.9% | 35.2% | 73.8% |
| Calibration error on AG News (lower is better) | 0.038 | 0.052 | 0.080 |
| Latency per question | ~5 ms (RTX 5050 laptop), ~55 ms (CPU) | ~10–19 ms (same GPU) | ~307 ms per case (API) |
- Fine-tuning closes the gap on specific tasks. With the same recipe, an earlier Naluri checkpoint fine-tuned for 17 minutes on typed-decisions' train split reached 72.5% vs Jev's 73.8%, a statistical tie (p = 0.29). That fine-tuned checkpoint is not this one.
- Zero-shot on structured business decisions (typed-decisions) is the main weakness. Jev is far ahead there.
- Why this checkpoint: minimal-pair training made the model genuinely use the state. On typed-decisions choice questions, its answer matches its empty-state answer 52% of the time, down from 93% for M5. A later run (M7) scored 48.6% but partly by shifting its default answer, so it was not released. Details are in the report.
Training
- Input format (the same layout as Laya):
<s> {type} question: {instructions} </s> <mask> option 1 <mask> option 2 … </s> {state} </s>. Each option is scored from the encoder vector at its own<mask>by a small MLP head, and a softmax over a question's options gives the answer distribution. - Objective: cross-entropy against one-hot labels or a teacher's soft probabilities, followed by one calibration temperature per question type.
- Recipe:
- M4: continue from M2 for 2 epochs, with task-balanced sampling (p ∝ √size), 3× weight on synthetic decisions, and public topic data
- M5: continue from M4 for 2 epochs (learning rate 1e-5 for the encoder, 1e-4 for the head), adding 10k operational decisions with 2× synthetic weight
- M6-pairs (this checkpoint): continue from M5 for 2 epochs, adding 5.2k contrastive minimal-pair decisions (weighted 3×): one question set, 3–4 near-identical states whose correct answers differ, so the model must read the state
- the epoch is selected on the mean of per-task dev accuracies (SIB-200 and XCOPA validation splits + both synthetic dev sets)
- about 50 minutes per stage on an 8 GB RTX 5050 laptop GPU
- Augmentation: options shuffled, labels renamed, instructions paraphrased, varying option counts, and noul, choice and score mixed.
- Weights: stored in fp16 (the encoder) and loaded in fp32. The largest probability difference against the fp32 training checkpoint was 0.0005.
Training data
About 107k typed questions. The licenses below are as listed on each dataset's card; check them yourself before any use.
| Dataset | Use | License (per dataset card) |
|---|---|---|
| XNLI | entailment (noul / choice) | CC BY-NC 4.0 |
MASSIVE (mteb/amazon_massive_intent) | intent (choice / noul) | CC BY 4.0 (original) / Apache 2.0 (mirror) |
| Amazon Reviews Multi (mirror) | review stars (score) + polarity | Amazon's own terms (non-commercial) |
| TextDetox multilingual toxicity | toxicity | OpenRAIL++ |
| papluca/language-identification | language ID | derived from Amazon Reviews, XNLI and STSb |
| dair-ai/emotion | emotion | other |
| RACE | reading comprehension | non-commercial research |
| UCI SMS Spam | spam | unknown on the Hub |
| MasakhaNEWS, Yahoo Answers Topics, DBpedia-14 | topic classification | see each dataset card |
| Synthetic decisions (5k) | business decisions in 17 languages and 45 domains | written by DeepSeek V4 Flash, labeled with probabilities by DeepSeek V4 Pro |
| Synthetic ops decisions (10k) | run and event logs in 30 operational domains (next action, outcome, risk, review) | written by DeepSeek V4 Flash, labeled with probabilities by DeepSeek V4 Pro |
| Synthetic minimal pairs (5.2k) | groups of near-identical states whose answers differ | written by DeepSeek V4 Flash, labeled with probabilities by DeepSeek V4 Pro |
The SIB-200 and XCOPA validation splits were used only to pick the epoch. No test split of any eval set was trained on.
License
These weights are released under CC BY-NC 4.0 (non-commercial), because several training datasets restrict commercial use. The code on GitHub is Apache 2.0. For commercial use, retrain on data you are licensed to use; the pipeline is in the GitHub repo.
Limitations
- Zero-shot on structured business decisions is weak: 46.9% on typed-decisions vs Jev's 73.8%. Choice questions like "which action should be taken?" are the weakest part (37%), and on some of them the model still leans on how the options are worded. Fine-tune for such tasks.
- Yes/no questions about structured JSON react only weakly to the data. For example, "should this transfer be held for fraud review?" got 0.09 for a Rp 45M transfer from a 2-day-old account on a new device vs 0.07 for a normal one: right direction, far too small a gap. Yes/no on text works well (an Indonesian cancellation threat 0.91 vs a thank-you message 0.02).
- English topic classification (AG News) trails Laya and Jev.
- Low-resource languages such as Yoruba are weaker.
- Inputs: a 512-token limit, and up to about 20 options per question.
- Seeds: this is a single run. For M4, two seeds agreed within about 1 point.
Acknowledgements
Built on XLM-RoBERTa. The input format and fine-tuning recipe follow Laya. Inspired by Contrastive Language Models and TypeSafe's Jev.