File size: 8,633 Bytes
55fd022
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
---
license: apache-2.0
library_name: transformers
pipeline_tag: text-classification
tags: [laya, system-one, calibrated-decisions, rlcd, classification, routing, scoring, guardrails, moderation, reinforcement-learning, commercial-use]
---

# Laya

Open-source, non-autoregressive **System 1 decision model**. Give it a **state** (text, email,
ticket, or JSON) and **typed questions**; it returns typed answers with calibrated probabilities
in a single forward pass. It never generates text, so there is nothing to parse and nothing to
hallucinate.

**This repo holds the English checkpoint** and serves as the hub for the family.

| checkpoint | encoder | params | context | use it for |
|---|---|---|---|---|
| **`convaiinnovations/laya`** (this repo) | ModernBERT-large | 421M | 512 | English |
| [`convaiinnovations/laya-multilingual`](https://huggingface.co/convaiinnovations/laya-multilingual) | mmBERT-base | 322M | 1024 | 100+ languages, ~2x faster |
| [`convaiinnovations/laya-typed-decisions`](https://huggingface.co/convaiinnovations/laya-typed-decisions) | ModernBERT-large | 421M | 1024 | the typed-decisions workflows |

| question type | returns |
|---|---|
| `choice` | selected option, probability per option, confidence |
| `score` | expected level on your ordinal rubric, distribution, confidence |
| `noul` | calibrated probability P(true) |

## Quickstart

```bash
pip install laya
```

```python
import laya

agent = laya.load("convaiinnovations/laya")
result = agent.predict(
    {"subject": "Duplicate charge on invoice 4411",
     "body": "We were billed twice for March. Please refund the duplicate."},
    {"department": {"type": "choice", "instructions": "Which team should handle this?",
                    "criteria": {"billing": "invoices, payments, refunds",
                                 "technical": "bugs and outages", "sales": "pricing"}},
     "urgency": {"type": "score", "instructions": "How urgent is this?",
                 "criteria": ["not urgent", "soon", "blocking"]},
     "churn_risk": {"type": "noul", "instructions": "Does the user threaten to cancel?"}},
)
print(result["answers"]["department"]["choice"])
```

### Routing between the three checkpoints

```python
from laya import Router

router = Router()                       # lazy-loads only what a request needs
router.predict({"body": "I was charged twice"}, questions)          # -> laya
router.predict({"body": "मुझसे दो बार शुल्क लिया गया"}, questions)   # -> laya-multilingual
router.predict(state, questions, model="typed-decisions")           # explicit
```

`Router` keeps one checkpoint resident by default (`max_loaded`); raise it for mixed-language
traffic so it does not reload on every language switch.

> **If `laya.load()` hangs:** `transformers` probes for TensorFlow at import, and when TF is
> installed its abseil runtime can deadlock model construction. Run with `USE_TF=0`.

## Architecture

- **Backbone** ModernBERT-large (395M, bidirectional, fully fine-tuned) + a decision head
  trained from scratch: 2 transformer layers, an option-marker scorer, and an act/escalate head.
  421M total.
- **Option markers** every option is scored at its own `[MASK]` token, then softmaxed over that
  question's options. The answer space is defined at request time, so new schemas need no
  retraining.
- **Budget** 512 tokens per question (question + options + state).
- **Batching** every question in a call is answered in one forward pass.

## Training

**RLCD (Reinforcement Learning for Calibrated Decisions).** The policy reports a distribution;
exploration adds zero-mean Gaussian noise to the logits; the reward is a strictly proper scoring
rule (log + spherical, plus ranked probability score for ordinal questions). Expected reward is
maximised only by reporting honest probabilities. Updates are REINFORCE with a group-mean
baseline (GRPO-style). Multi-turn conversations use TD(λ=1.0) over prefix slices.

7,313 updates, 1 epoch, ~1.96 h. Fitted temperatures `[1.637, 1.251, 1.983]` with
per-option-count scaling.

## Benchmarks

Measured on a Tesla T4; every checkpoint answered byte-identical questions in the same run.

### Speed

| questions/call | `laya` | `laya-multilingual` |
|---|---|---|
| 1 | 39.5 ms | **32.8 ms** |
| 10 | 158.6 ms (15.9 ms/q) | **72.3 ms (7.2 ms/q)** |
| 50 | 771 ms | **337 ms (6.8 ms/q)** |

103–332 questions/sec batched. TypeSafe Jev has been independently measured at 236–276 ms p50
([AbdelStark](https://github.com/AbdelStark/jev-benchmarks),
[nibzard](https://github.com/nibzard/decision-model-benchmark)), so Laya answers a single
question roughly **6–7× faster**.

### Against Jev

Jev figures are **published by third parties, not measured here** — no TypeSafe API access.
Sample sizes and prompts differ; treat as indicative, not a controlled head-to-head.

| dataset | Jev | Laya | |
|---|---|---|---|
| **typed-decisions** (2,000 decisions) | 0.727 | **0.766** | `laya-typed-decisions` |
| AG News (4 labels) | 0.910 | **0.950** | `laya` |
| DAIR Emotion (6) | 0.480 · Brier 0.846 · NLL 5.588 | **0.595** | `laya`, held out |
| ECE | 0.246 | **0.081** | after temperature fitting |

On DAIR Emotion, Jev assigned zero probability to the true label on 16% of examples.

### typed-decisions, all three checkpoints

400 cases, 2,000 decisions, four workflows — measured here.

| model | accuracy | soft acc | Brier | ECE | score MAE |
|---|---|---|---|---|---|
| **`laya-typed-decisions`** | **0.766** | 0.471 | **0.062** | 0.213 | **0.242** |
| `laya` | 0.362 | 0.332 | 0.316 | 0.175 | 0.694 |
| `laya-multilingual` | 0.342 | 0.326 | 0.439 | 0.285 | 0.687 |
| *Jev 1.13.0 (published)* | *0.727* | *0.580* | *0.148* | *0.144* | *0.391* |
| *teacher ceiling* | *0.735* | | | | |
| *majority class* | *0.461* | | | | |

The fine-tuned checkpoint clears the teacher ceiling and wins all four workflows: invoice
processing 0.804, security incidents 0.766, customer service 0.764, agent-trace observability
0.730. By primitive: `noul` 0.857, `choice` 0.733, `score` 0.723.

The base checkpoints sit below the majority-class baseline here — the capability on this
benchmark comes from fine-tuning, which is what the
[fine-tuning notebook](https://github.com/NandhaKishorM/laya/blob/main/notebooks/laya_finetune_typed_decisions_2xT4_kaggle.ipynb)
is for.

### English tasks

| task | `laya` | `laya-multilingual` | |
|---|---|---|---|
| AG News | **0.947** | 0.937 | in training mix |
| BoolQ | **0.830** | 0.787 | in training mix |
| DAIR Emotion | **0.573** | 0.513 | held out |
| prompt-injections | **0.698** | 0.578 | held out, n=116 |
| SST-5 (ordinal) | 0.372 | 0.282 | held out |

### Languages — use `laya-multilingual` outside English

Across 51 languages on MASSIVE intent (20 options, random = 0.050), this checkpoint
macro-averages **0.227** with macro ECE **0.733**, clearing 3× random on only 23 of 51.
Khmer scores **0.000 accuracy at 0.952 confidence**.

| | `laya` | `laya-multilingual` |
|---|---|---|
| MASSIVE intent, English | **0.783** | 0.657 |
| MASSIVE intent, 13 others | 0.306 | **0.451** |
| XNLI, English | **0.860** | 0.843 |
| XNLI, 14 others | 0.521 | **0.731** |

The confidence score gives no warning when the input is unreadable, so the choice has to be made
before the forward pass — that is what `Router` is for.

## Limits

- **Near chance on typed-decisions zero-shot** — 0.362 here and 0.352 for multilingual, against a
  0.318 random and 0.461 majority-class baseline. The 0.766 belongs to the checkpoint fine-tuned
  on that benchmark's own training split. Laya is a fast base to specialise, not a zero-shot
  decision engine.
- **Keep `choice` questions under ~20 options.** Options share a fixed `head_max_len` budget
  (192 tokens here), so a very large label space leaves only a few tokens per label and
  accuracy falls off sharply. Split into a coarse choice then a fine one.
- Ordinal `score` questions are the weakest primitive (SST-5 0.372).
- Ships over-confident: refitting one temperature per (question type, option count) moves mean
  ECE **0.466 → 0.081**. Do this on your own data before trusting the probabilities.
- English only. Use `laya-multilingual` for anything else.

## Links

- **GitHub** https://github.com/NandhaKishorM/laya
- **PyPI** https://pypi.org/project/laya/
- **Demo** https://huggingface.co/spaces/convaiinnovations/laya-demo
- **Write-up** [dev.to](https://dev.to/nandakishor_m_6cc0adfde9f/i-built-non-autoregressive-decision-models-a-year-ago-then-a-frontier-lab-called-it-a-18me)

Apache 2.0 · Convai Innovations