Laya typed-decision inference engine for MoonBit — native ModernBERT/mmBERT encoder, no Python runtime
| type | answer |
|---|---|
| choice | the top label, plus a probability per label |
| score | a probability-weighted level on an ordinal scale |
| noul | a calibrated probability that a statement holds |
mkdir -p models/laya-multilingual-mlx
cd models/laya-multilingual-mlx
BASE=https://huggingface.co/aac6fef/laya-multilingual-mlx/resolve/main
curl -sLO $BASE/model.safetensors
curl -sLO $BASE/rl_agent_config.json
mkdir -p encoder tokenizer
curl -sL $BASE/encoder/config.json -o encoder/config.json
curl -sL $BASE/tokenizer/tokenizer.json -o tokenizer/tokenizer.json
curl -sL $BASE/tokenizer/tokenizer_config.json -o tokenizer/tokenizer_config.json<directory>/model.safetensors
<directory>/rl_agent_config.json
<directory>/encoder/config.json
<directory>/tokenizer/tokenizer.json
<directory>/tokenizer/tokenizer_config.json| repository | encoder | params | context |
|---|---|---|---|
| aac6fef/laya-mlx | ModernBERT-large | 421M | 512 |
| aac6fef/laya-multilingual-mlx | mmBERT-base | 322M | 1024 |
| aac6fef/laya-typed-decisions-mlx | ModernBERT-large | 421M | 1024 |
moon add majikxu/laya///|
fn triage() -> Unit raise {
// `backend` defaults to Metal, falling back to the CPU when there is no GPU.
// Pass `backend=Cpu` to force it; `agent.device()` reports what you got.
let agent = @laya.Agent::load("models/laya-multilingual-mlx")
let state = "I was billed twice this month. Please refund the duplicate charge."
let prediction = agent.predict(state, [
(
"department",
@laya.Question::choice("Who should handle this?", [
"billing", "technical", "sales",
]),
),
(
"urgency",
@laya.Question::score("How urgent is this?", [
"not urgent", "soon", "urgent", "critical",
]),
),
("refund", @laya.Question::noul("The customer is asking for a refund.")),
])
for entry in prediction.answers {
let (id, answer) = entry
match answer.decision {
Choice(label~, ..) => println("\{id}: \{label} (\{answer.confidence})")
Score(value~, ..) => println("\{id}: \{value} (\{answer.confidence})")
Noul(probability~) => println("\{id}: \{probability}")
}
}
}@laya.Question::choice_described("What should the billing system do next?", [
("dunning", "send a payment reminder"),
("suspend", "suspend the workspace"),
("wait", "take no action yet"),
])[CLS] <type> question: <instructions> [SEP] [MASK] <option 0> [MASK] <option 1> … [SEP] <state> [SEP]moon build --target native --release
./_build/native/release/build/cmd/laya/laya.exe \
--model models/laya-multilingual-mlx \
--state "I was billed twice this month. Please refund the duplicate charge." \
--questions examples/questions.json{
"department": {
"type": "choice",
"instructions": "Who should handle this?",
"criteria": ["billing", "technical", "sales"]
},
"refund": {
"type": "noul",
"instructions": "The customer is asking for a refund."
}
}| checkpoint | prompt tokens | CPU | Metal | speedup |
|---|---|---|---|---|
| multilingual (322M) | 23 | 19.5 ms | 36.3 ms | 0.54× |
| multilingual | 79 | 45.4 ms | 25.6 ms | 1.77× |
| multilingual | 304 | 219 ms | 51.6 ms | 4.25× |
| multilingual | 1024 | 1609 ms | 157 ms | 10.3× |
| large (421M) | 23 | 46.7 ms | 72.2 ms | 0.65× |
| large | 79 | 102 ms | 58.2 ms | 1.76× |
| large | 304 | 465 ms | 122 ms | 3.82× |
| large | 512 | 1046 ms | 217 ms | 4.83× |
src/ facade: Agent, Question, Answer, Prompt, Vocabulary
src/error/ LayaError, shared by every layer
src/tensor/ the numeric layer, both backends:
laya_native.c BLAS, float16 decoding, checkpoint reads
ops.mbt CPU kernels (layer norm, GELU, RoPE, softmax)
laya_metal.m Metal device, buffers, dispatch, MPS matmul
metal/laya_kernels.metal GPU kernels, one per CPU kernel
src/safetensors/ safetensors header parsing and typed tensor reads
src/model/ config parsing, the weight arena, both forward passes
src/cmd/laya/ the CLI
ref/ Python reference runtime and fixture generatorscd ref && uv sync # installs laya-mlx as the oracle
uv run python dump_fixtures.py ../models/laya-multilingual-mlx \
../fixtures/laya-multilingual.json --full
uv run python dump_fixtures.py ../models/laya-mlx ../fixtures/laya-en.json --full
uv run python dump_tokenizer_fixtures.py ../models/laya-multilingual-mlx/tokenizer \
../fixtures/tokenizer-multilingual.json
uv run python dump_tokenizer_fixtures.py ../models/laya-mlx/tokenizer \
../fixtures/tokenizer-en.json
cd .. && moon test --target native///|
test "options render as the model sees them" {
let choice = @laya.Question::choice_described("Who should handle this?", [
("billing", "payments and invoices"),
("technical", "bugs and outages"),
])
debug_inspect(
choice.rendered_options(),
content=(
#|["billing: payments and invoices", "technical: bugs and outages"]
),
)
let score = @laya.Question::score("How urgent?", ["not urgent", "critical"])
debug_inspect(
score.rendered_options(),
content=(
#|["level 0: not urgent", "level 1: critical"]
),
)
let noul = @laya.Question::noul("The customer wants a refund.")
debug_inspect(
noul.rendered_options(),
content=(
#|[
#| "false: no, the statement does not hold",
#| "true: yes, the statement holds",
#|]
),
)
inspect(noul.type_name(), content="noul")
inspect(noul.type_index(), content="2")
}| type | answer |
|---|---|
| choice | the top label, plus a probability per label |
| score | a probability-weighted level on an ordinal scale |
| noul | a calibrated probability that a statement holds |
mkdir -p models/laya-multilingual-mlx
cd models/laya-multilingual-mlx
BASE=https://huggingface.co/aac6fef/laya-multilingual-mlx/resolve/main
curl -sLO $BASE/model.safetensors
curl -sLO $BASE/rl_agent_config.json
mkdir -p encoder tokenizer
curl -sL $BASE/encoder/config.json -o encoder/config.json
curl -sL $BASE/tokenizer/tokenizer.json -o tokenizer/tokenizer.json
curl -sL $BASE/tokenizer/tokenizer_config.json -o tokenizer/tokenizer_config.json<directory>/model.safetensors
<directory>/rl_agent_config.json
<directory>/encoder/config.json
<directory>/tokenizer/tokenizer.json
<directory>/tokenizer/tokenizer_config.json| repository | encoder | params | context |
|---|---|---|---|
| aac6fef/laya-mlx | ModernBERT-large | 421M | 512 |
| aac6fef/laya-multilingual-mlx | mmBERT-base | 322M | 1024 |
| aac6fef/laya-typed-decisions-mlx | ModernBERT-large | 421M | 1024 |
moon add majikxu/laya///|
fn triage() -> Unit raise {
// `backend` defaults to Metal, falling back to the CPU when there is no GPU.
// Pass `backend=Cpu` to force it; `agent.device()` reports what you got.
let agent = @laya.Agent::load("models/laya-multilingual-mlx")
let state = "I was billed twice this month. Please refund the duplicate charge."
let prediction = agent.predict(state, [
(
"department",
@laya.Question::choice("Who should handle this?", [
"billing", "technical", "sales",
]),
),
(
"urgency",
@laya.Question::score("How urgent is this?", [
"not urgent", "soon", "urgent", "critical",
]),
),
("refund", @laya.Question::noul("The customer is asking for a refund.")),
])
for entry in prediction.answers {
let (id, answer) = entry
match answer.decision {
Choice(label~, ..) => println("\{id}: \{label} (\{answer.confidence})")
Score(value~, ..) => println("\{id}: \{value} (\{answer.confidence})")
Noul(probability~) => println("\{id}: \{probability}")
}
}
}@laya.Question::choice_described("What should the billing system do next?", [
("dunning", "send a payment reminder"),
("suspend", "suspend the workspace"),
("wait", "take no action yet"),
])[CLS] <type> question: <instructions> [SEP] [MASK] <option 0> [MASK] <option 1> … [SEP] <state> [SEP]moon build --target native --release
./_build/native/release/build/cmd/laya/laya.exe \
--model models/laya-multilingual-mlx \
--state "I was billed twice this month. Please refund the duplicate charge." \
--questions examples/questions.json{
"department": {
"type": "choice",
"instructions": "Who should handle this?",
"criteria": ["billing", "technical", "sales"]
},
"refund": {
"type": "noul",
"instructions": "The customer is asking for a refund."
}
}| checkpoint | prompt tokens | CPU | Metal | speedup |
|---|---|---|---|---|
| multilingual (322M) | 23 | 19.5 ms | 36.3 ms | 0.54× |
| multilingual | 79 | 45.4 ms | 25.6 ms | 1.77× |
| multilingual | 304 | 219 ms | 51.6 ms | 4.25× |
| multilingual | 1024 | 1609 ms | 157 ms | 10.3× |
| large (421M) | 23 | 46.7 ms | 72.2 ms | 0.65× |
| large | 79 | 102 ms | 58.2 ms | 1.76× |
| large | 304 | 465 ms | 122 ms | 3.82× |
| large | 512 | 1046 ms | 217 ms | 4.83× |
src/ facade: Agent, Question, Answer, Prompt, Vocabulary
src/error/ LayaError, shared by every layer
src/tensor/ the numeric layer, both backends:
laya_native.c BLAS, float16 decoding, checkpoint reads
ops.mbt CPU kernels (layer norm, GELU, RoPE, softmax)
laya_metal.m Metal device, buffers, dispatch, MPS matmul
metal/laya_kernels.metal GPU kernels, one per CPU kernel
src/safetensors/ safetensors header parsing and typed tensor reads
src/model/ config parsing, the weight arena, both forward passes
src/cmd/laya/ the CLI
ref/ Python reference runtime and fixture generatorscd ref && uv sync # installs laya-mlx as the oracle
uv run python dump_fixtures.py ../models/laya-multilingual-mlx \
../fixtures/laya-multilingual.json --full
uv run python dump_fixtures.py ../models/laya-mlx ../fixtures/laya-en.json --full
uv run python dump_tokenizer_fixtures.py ../models/laya-multilingual-mlx/tokenizer \
../fixtures/tokenizer-multilingual.json
uv run python dump_tokenizer_fixtures.py ../models/laya-mlx/tokenizer \
../fixtures/tokenizer-en.json
cd .. && moon test --target native///|
test "options render as the model sees them" {
let choice = @laya.Question::choice_described("Who should handle this?", [
("billing", "payments and invoices"),
("technical", "bugs and outages"),
])
debug_inspect(
choice.rendered_options(),
content=(
#|["billing: payments and invoices", "technical: bugs and outages"]
),
)
let score = @laya.Question::score("How urgent?", ["not urgent", "critical"])
debug_inspect(
score.rendered_options(),
content=(
#|["level 0: not urgent", "level 1: critical"]
),
)
let noul = @laya.Question::noul("The customer wants a refund.")
debug_inspect(
noul.rendered_options(),
content=(
#|[
#| "false: no, the statement does not hold",
#| "true: yes, the statement holds",
#|]
),
)
inspect(noul.type_name(), content="noul")
inspect(noul.type_index(), content="2")
}type Agent<directory>/model.safetensors
<directory>/rl_agent_config.json
<directory>/encoder/config.json
<directory>/tokenizer/tokenizer.json
<directory>/tokenizer/tokenizer_config.jsonpub(all) enum Question {
Choice(instructions~ : String, options~ : Array[ChoiceOption])
Score(instructions~ : String, levels~ : Array[String])
Noul(instructions~ : String, when_false~ : String?, when_true~ : String?)
} derive(Eq, Debug)type Vocabularyfn build_prompt(vocabulary : Vocabulary, state : String, question : Question, max_len~ : Int, head_max_len~ : Int) -> Prompt raise LayaErrorInstall
Download zipLaya typed-decision inference engine for MoonBit — native ModernBERT/mmBERT encoder, no Python runtime