Open weights on Hugging Face · Apache 2.0

We fine-tuned a modelthat speaks generative UI.

MDMA is designed to work on small models. To find out how small, we trained one. It reads a one-line DSL intent and returns a valid MDMA document — and it passes every case in the gate we run against it.

MobileReality/mdma-gemma4-26b-dsl-unsloth-v1

What it is

A domain-specific fine-tune of Google's Gemma 4, trained on one job and nothing else: read an MDMA-IL DSL intent, write the MDMA document it describes. The LoRA is merged into the base weights, so it is one model to download and serve — not a base plus an adapter.

Base model
Gemma 4 26B-A4B (Instruct)
Parameters
27B
Method
LoRA, merged into the base weights
Published as
BF16 safetensors
Served as
INT8, OpenAI-compatible endpoint
License
Apache 2.0

Built on unsloth/gemma-4-26B-A4B-it. A modified derivative of Google's Gemma 4, distributed under the Apache License 2.0.

One line in, a working component out

The model does not read prose. It reads MDMA-IL: a compact intent language, one line per component. That is what makes it cheap — the prompt is small, the input is small, and the output is the only part that has to be right.

Input — a DSL intent
form#contact[full-name*:t, email*^:e](action=contact-submitted)

A contact form with a required name and a required email flagged as personal data, submitting the contact-submitted action.

Output — an MDMA component
type: form
id: contact
fields:
  - name: full-name
    type: text
    label: "Full Name"
    required: true
  - name: email
    type: email
    label: "Email"
    required: true
    sensitive: true
onSubmit: contact-submitted

Renderable as-is by any of the three renderers. The ^ became sensitive: true, which is what drives PII redaction in the runtime.

The grammar it reads
<type>#<id>[<field>, <field>, ...](<prop>, <prop>, ...)

field = <name>[*][^]:<typecode>[{opt1|opt2|...}]
        *  = required        ^  = sensitive (PII)
        typecode: t=text  n=number  e=email  d=date
                  s=select  c=checkbox  ta=textarea  f=file
props = text="..."  |  action=<id>  |  variant=<name>

Gated, not vibe-checked

The model ships behind the same PromptFoo suites the third-party models run, plus a holdout gate of 95 scenarios it never saw in training. Every run below is committed to the repository as JSON.

Committed eval runs

181/181 cases · 0 failures · 0 errors

SuiteCasesPassedWhat it asserts
DSL holdout gate9595Every output parses and validates as an MDMA document
Author2828Correct component, exact field count, PII flags
Flows1515Multi-step example flows
Fixer1515Repairs a broken document to valid output
Guidance1515Calls the generate_mdma tool correctly
Custom prompt1313Author prompt layered with a custom one

An earlier and much smaller fine-tune, on Gemma 4 E4B with a 2048-token context, topped out around 90.5% valid on the same holdout. The jump to the 26B base is what closed the gap.

The serving contract

This is a narrow model, and it behaves like one. Four settings separate clean output from a Gemma 4 reasoning loop — none of them optional.

System promptmobile-reality/mdma-il, byte-for-byte

The model was tuned against this exact prompt. A different one is out of distribution and quality drops.

enable_thinkingfalse

Sent as a chat template argument. Left on, the reasoning channel leaks into the output.

temperature0

Greedy decoding for the deterministic DSL mode. The conversational mode runs at 1.

min_p / repetition_penalty0.02 / 1.1

The repetition guard for Gemma 4's reasoning-loop failure mode.

Two modes
  • Deterministic. The DSL intent in, greedy decoding, one document out. Reproducible.
  • Conversational. Sampling at temperature 1 with tool calling, so an agent can hold a dialogue and reach for a generate_mdma tool when it needs UI.

Each mode has its own system prompt. Both ship in @mobile-reality/mdma-prompt-pack.

Why bother training one

Every generative-UI format claims it works on small models. The honest test is whether you would bet your own weights on it.

A strict JSON UI contract falls apart at this size — one unbalanced brace and the whole payload is unusable, so teams quietly fall back to the largest tier and pay for it on every request. MDMA is forgiving Markdown with a schema behind it, which is exactly the shape a small model can hold together. A 27B open-weights model, served quantized, emits it reliably enough to gate on.

Which means the interesting number is not the model. It is what the model implies: you can run generative UI on hardware you control, at a cost that does not scale with your traffic, on weights nobody can deprecate under you.