Skip to content

AMALIA-9B (European Portuguese) support: SPM Metaspace prepend, ChatML detection, bf16 compute - #44

Merged
hvasconcelos merged 1 commit into
masterfrom
amalia-9b-support
Jul 5, 2026
Merged

AMALIA-9B (European Portuguese) support: SPM Metaspace prepend, ChatML detection, bf16 compute#44
hvasconcelos merged 1 commit into
masterfrom
amalia-9b-support

Conversation

@hvasconcelos

Copy link
Copy Markdown
Owner

Adds support for layerx-labs/AMALIA-9B-0626-DPO-MLX-4bit (and the SFT variant — identical architecture/tokenizer/template), the European-Portuguese LLM built on EuroLLM-9B (PROPOR 2026 paper). Golden-gated end-to-end against mlx-lm (reference/fixtures_amalia/).

What's in it

The forward pass needed no new model class — AMALIA is a plain LlamaForCausalLM (42 layers, GQA 32/8, head_dim 128, rope_theta 1e6, no rope scaling), so create_model falls through to the stock LlamaModel. The support work is in three areas:

Tokenizer (tokenizer/spm)

  • The SPM backend now honors the Metaspace pre-tokenizer's prepend_scheme: "always" prepends to the input-leading segment only — never after special tokens, never doubled onto a leading space. The exact semantics were settled empirically by the golden corpus (33 strings incl. post-special, adjacent-special, and byte-fallback cases); Gemma (never) is regression-gated unchanged. "first" throws rather than approximating.
  • The decoder chain's Strip step (drop N leading spaces on decode) is parsed and applied, so decode(encode(s)) == s.

Chat format (tokenizer, runtime/engine)

  • New ChatFormat::Amalia: ChatML with the checkpoint's default Portuguese "Amália" system prompt. model_type is "llama", so the new chat_format_for_model_dir detects the format from the checkpoint's chat_template.jinja (falling back to the model_type mapping).
  • AMALIA's BOS (id 3) is <|im_start|>: apply_chat_template encodes the fully-rendered template without the BOS prepend, mirroring HF's add_special_tokens=False. Gated byte-exact by the chat_ids fixture.

bf16 compute path (core/config, core/weights, model, runtime)

The headline discovery: this family's residual stream overflows fp16 from layer 9 — an fp16 forward is all-NaN logits, and the fp16 mlx-lm reference is equally degenerate (the first fixture dump produced NaN goldens that gated nothing). Fixes:

  • ModelConfig::bf16_compute, selected by the family's config fingerprint (no config field can drive this — every MLX conversion declares dtype: bfloat16, including the fp16-proven Llama/Qwen checkpoints). The weight cast, batched additive mask, and prefill logit placeholders all follow the new compute_dtype(cfg) helper; skinny_mm self-disables (fp16-only kernels). Every other checkpoint keeps fp16 bit-for-bit (full regression suite passes untouched).
  • KV-cache quantization and the prefix cache are rejected at engine creation for bf16-compute models (no golden coverage yet) — never a silent fallback.
  • compare_close now fails on nonfinite values: NaN-vs-NaN previously passed vacuously, which is exactly how the degenerate fp16 reference slipped through.
  • config.json quantization.mode != "affine" is rejected at load (a future non-affine repo would otherwise run silent garbage through mx::quantized_matmul).

Validation

  • 294/294 tests pass with all gated models present (Llama-3.2 fp16/4-bit/GGUF, Qwen3 dense/yarn/MoE/3.5/VL, Gemma, Qwen3-Embedding, AMALIA).
  • New AMALIA gates: tokenizer corpus byte-exact, chat_ids exact, embeddings/front-half/block0 close, argmax exact, 20-token greedy stream exact (raw-logit closeness is deliberately loose: bf16 over 4-bit quantized matmuls is fusion-context-sensitive, per the kv-quant precedent).
  • CLI smoke: mlxforge-cli generate layerx-labs/AMALIA-9B-0626-DPO-MLX-4bit "Qual é a capital de Portugal? E diz-me uma curiosidade sobre ela." 120 → fluent pt-PT answer about Lisboa, clean EOS stop (eos [4, 2]) after 96 tokens, 21 tok/s decode.

Docs updated: doc/supported-models.md, doc/tokenizer.md, README, CHANGELOG, CLAUDE.md gotchas. Future work noted: an engine-level chat-format/compute-dtype override (ABI append), kv-quant + prefix-cache goldens for bf16 models.

🤖 Generated with Claude Code

…L template detection, bf16 compute

Adds layerx-labs/AMALIA-9B-0626-{DPO,SFT}-MLX-4bit (EuroLLM-9B base), golden-gated
against mlx-lm (reference/fixtures_amalia/). The forward pass is the stock
LlamaModel; the new pieces:

- tokenizer/spm: honor the Metaspace pre-tokenizer's prepend_scheme ("always"
  prepends U+2581 to the input-leading segment only — never after special
  tokens, never doubled onto a leading space; pinned by the corpus fixture) and
  the decoder Strip step (drop N leading spaces on decode). Gemma unchanged.
- tokenizer: ChatFormat::Amalia (ChatML + default Portuguese system prompt),
  detected from the checkpoint's chat_template.jinja via the new
  chat_format_for_model_dir (model_type is "llama", so the type mapping can't
  identify it). AMALIA's BOS id 3 IS <|im_start|>, so apply_chat_template
  encodes the fully-rendered template without the BOS prepend (HF
  add_special_tokens=False semantics).
- bf16 compute path (ModelConfig::bf16_compute): this family's residual stream
  overflows fp16 from layer 9 — an fp16 forward is all-NaN logits, and the fp16
  mlx-lm reference is equally degenerate. Selected by the family's config
  fingerprint; the weight cast, batched mask, and prefill logit placeholders
  follow compute_dtype(cfg); skinny_mm self-disables (fp16-only); kv-quant and
  the prefix cache are rejected at engine creation for bf16 models (ungated).
  Every other checkpoint keeps fp16 bit-for-bit.
- test harness: compare_close now fails on nonfinite values (NaN-vs-NaN
  previously passed vacuously — exactly how a degenerate fp16 reference slipped
  through); config.json quantization "mode" != affine is rejected at load.

Gates: tokenizer corpus byte-exact (33 strings incl. post-special and
byte-fallback cases), chat_ids exact, front-half/block0 close, argmax exact,
20-token greedy stream exact. CLI smoke: fluent pt-PT answer, EOS stop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hvasconcelos
hvasconcelos merged commit 6e60b8d into master Jul 5, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant