LoRA and aLoRA adapters
Off-the-shelf language models sometimes fail on domain-specific tasks — particularly requirement validation over proprietary terminology or specialized classification schemes not well-represented in general training data. Mellea lets you train a LoRA or aLoRA adapter on your own labeled dataset and use it as a requirement validator in any Mellea program.
Prerequisites: pip install "mellea[cli,hf]". Training requires a GPU or
Apple Silicon Mac with sufficient VRAM for the chosen base model. Uploading requires a
Hugging Face account.
Backend note: Custom-trained adapters can only be loaded into
LocalHFBackend. They do not work with Ollama, OpenAI, or other remote backends.Granite Switch models ship with pre-trained adapter functions embedded in the model weights, which can be used via
OpenAIBackendwithload_embedded_adapters=True. See Adapter functions for details.
LoRA vs aLoRA
Both adapter types fine-tune a base model on your data. The difference is inference cost:
| LoRA | aLoRA | |
|---|---|---|
| Inference overhead | Processes full context each call | Activated at a single token — minimal overhead |
| Best for | General fine-tuning | Fast inner-loop checks, requirement validation |
| Training time | Similar | Similar |
For requirement validation in Mellea (short binary checks inside a generation loop),
aLoRA is the better choice. Use --adapter lora if you need a more general fine-tune
and can absorb the inference cost.
Data format
Training data is a .jsonl file with one JSON object per line. Each object must have:
item— the input text to classifylabel— the string classification label
{"item": "Observed black soot on intake. Seal seems compromised under thermal load.", "label": "piston_rings"}
{"item": "Rotor misalignment caused torsion on connecting rod. High vibration at 3100 RPM.", "label": "connecting_rod"}
{"item": "Combustion misfire traced to a cracked mini-carburetor flange.", "label": "mini_carburetor"}
{"item": "Stembolt makes a whistling sound and does not complete the sealing process.", "label": "no_failure"}
Labels can be any strings. The adapter learns to predict the label from the item text.
Train an adapter
m alora train data.jsonl \
--basemodel ibm-granite/granite-3.2-8b-instruct \
--outfile ./checkpoints/my_adapter \
--adapter alora \
--epochs 6 \
--learning-rate 6e-6 \
--batch-size 2 \
--max-length 1024 \
--grad-accum 4
The trained adapter weights are saved to ./checkpoints/my_adapter/.
Parameters
| Flag | Type | Default | Description |
|---|---|---|---|
datafile | str | required | Path to .jsonl training file |
--basemodel | str | required | Hugging Face model ID or local path |
--outfile | str | required | Directory to save adapter weights |
--adapter | str | alora | Adapter type: alora or lora |
--device | str | auto | Device: auto, cpu, cuda, or mps |
--epochs | int | 6 | Number of training epochs |
--learning-rate | float | 6e-6 | Learning rate |
--batch-size | int | 2 | Per-device batch size |
--max-length | int | 1024 | Max tokenized sequence length |
--grad-accum | int | 4 | Gradient accumulation steps |
--promptfile | str | None | JSON file overriding the invocation prompt |
The default invocation prompt is <|start_of_role|>check_requirement<|end_of_role|>.
Provide --promptfile only if your adapter needs a different prompt format. The file
must contain {"invocation_prompt": "..."}.
Upload to Hugging Face
huggingface-cli login # one-time setup
m alora upload ./checkpoints/my_adapter \
--name your-org/my-adapter
This creates the Hugging Face repository if it does not exist and uploads the adapter
weights. Requires HF_TOKEN set or a prior huggingface-cli login.
Warning: Before uploading to a public repository, review whether your training data includes proprietary, confidential, or personal information. Language models can memorize details from small domain-specific datasets.
If you intend to use the adapter as a Mellea adapter function (so that it can be loaded by
model ID rather than local path), pass --intrinsic and provide an io.yaml file:
m alora upload ./checkpoints/my_adapter \
--name your-org/my-adapter \
--intrinsic \
--io-yaml ./io.yaml
Use the adapter in Mellea
Load the trained adapter into a LocalHFBackend using CustomIntrinsicAdapter.
Note:
CustomIntrinsicAdapteris deprecated in favor of theAdapter/WeightsBindingmodel, but is still the only working way to load a locally-trained custom adapter —Adapter's weight-loading is not yet implemented. This example will move toAdapteronce that lands; see #1144.
from mellea.backends.huggingface import LocalHFBackend
from mellea.backends.adapters.adapter import CustomIntrinsicAdapter
from mellea.stdlib.context import ChatContext
from mellea import MelleaSession
from mellea.stdlib.requirements import ALoraRequirement
backend = LocalHFBackend(model_id="ibm-granite/granite-3.2-8b-instruct")
adapter = CustomIntrinsicAdapter(
model_id="your-org/my-adapter", # HF repo ID or local checkpoint path
base_model_name="granite-3.2-8b-instruct",
intrinsic_name="custom-failure-check",
)
backend.add_adapter(adapter)
m = MelleaSession(backend, ctx=ChatContext())
failure_check = ALoraRequirement(
"The failure mode must not be 'no_failure'.",
intrinsic_name="custom-failure-check",
)
result = m.instruct(
"Write a triage summary based on this technician note: {{note}}",
user_variables={"note": "High vibration at 3100 RPM, connecting rod suspected."},
requirements=[failure_check],
)
print(str(result))
# Output will vary — LLM responses depend on model and temperature.
Note:
CustomIntrinsicAdapteremits an advisoryUserWarningbecause custom capability names are not part of Mellea's built-in capability registry. The adapter is still registered for routing.
ALoraRequirement routes validation through the adapter with the matching
intrinsic_name. Create CustomIntrinsicAdapter before the requirement: its
compatibility shim registers the custom name, which lets ALoraRequirement
resolve it. The adapter runs at the check_requirement prompt position. Its
io.yaml must transform the output into the
{"requirement_check": {"score": <float>}} response schema; label-only
adapter output is not compatible with ALoraRequirement.
How automatic routing works
When an adapter is loaded via backend.add_adapter(), Mellea automatically routes
req() validation calls through it rather than falling back to LLM-as-a-judge. The
rule is: use the most specific available method. In practice this means the aLoRA
adapter is preferred whenever one is loaded, with three exceptions:
backend.default_to_constraint_checking_alorais set toFalse— the adapter is loaded but routing is suppressed for the entire backend instance.- The requirement uses the
LLMaJRequirementsubtype explicitly — the caller is asking for LLM-as-a-judge regardless of what adapters are loaded. - The adapter is unavailable (e.g. cannot be loaded) — Mellea falls back to LLM-as-a-judge automatically. This is the only fallback case: if the adapter runs but its output fails schema validation, the error propagates rather than silently falling back.
If you want to force the adapter path even when using generate_from_context
directly (bypassing the normal validate() call), use ALoraRequirement from
mellea.stdlib.requirements — this bypasses default_to_constraint_checking_alora,
but still requires a matching adapter to actually be registered. If none is found,
Mellea logs a warning and falls back to regular generation rather than erroring.
Disable adapter validation
To run without adapter validation (for benchmarking or debugging):
backend.default_to_constraint_checking_alora = False
Set it back to True to re-enable. This flag is per-backend instance and does not
affect other sessions.
See also: Adapter functions | The Requirements System | Write Custom Verifiers | CLI Reference