Tutorial: Adding a Custom Adapter Function
This tutorial walks through registering a custom, non-catalog adapter
function by composing an Adapter directly — the replacement for the
deprecated CustomIntrinsicAdapter shim (Epic #929, issue #1144).
By the end you will have covered:
- Composing an
Adapterfrom anIdentity, an output contract, and aLocalFileBinding - Why a custom adapter needs an explicit
revision - Registering it with a backend and invoking it through
Intrinsic - Validating output with
ALoraRequirementagainst a custom adapter name
Prerequisites: pip install "mellea[hf]", a trained LoRA/aLoRA adapter
uploaded to Hugging Face Hub with
m alora upload ... --intrinsic --io-yaml ./io.yaml (see
LoRA and aLoRA adapters for
training and uploading), and a GPU or Apple Silicon Mac.
Step 1: Compose the adapter
Mellea's built-in adapter functions (answerability, requirement-check,
etc.) are looked up from a catalog that supplies their repo_id and
revision. A custom adapter has no catalog entry, so you supply those
fields yourself:
Repo layout matters:
LocalFileBindingonly finds weights at<name>/<base-model>/<lora|alora>/insiderepo_id— the same layout Mellea's own built-in adapter functions use, and the onem alora upload --intrinsic --io-yaml ./io.yamlproduces. The plainm alora upload(no--intrinsic) pushes weights to the repo root instead, whichLocalFileBindingcannot find — if you used that command, re-upload with--intrinsicbefore continuing.
# Requires: mellea[hf]
from mellea.backends.adapters import Adapter, Identity, LocalFileBinding, get_io_contract
from mellea.backends.adapters.catalog import AdapterType
def custom_failure_check_adapter() -> Adapter:
return Adapter(
identity=Identity(name="custom-failure-check", adapter_type="alora"),
# get_io_contract falls back to a permissive dict contract for names
# outside the built-in catalog — sufficient here, since the real
# output shape is enforced by the adapter's own io.yaml, not by
# IOContract (which doesn't yet drive request/response handling).
io_contract=get_io_contract("custom-failure-check"),
weights=LocalFileBinding(
name="custom-failure-check",
adapter_type=AdapterType.ALORA,
repo_id="your-org/my-adapter", # your Hugging Face repo
# A LocalFileBinding with revision=None resolves it from the
# catalog — which has no entry for a custom name. Pin it
# explicitly (a branch, tag, or commit SHA).
revision="main",
),
)
Trap: Omitting
revisionhere raisesValueError: Unknown intrinsic name '...'from deep insideLocalFileBinding.resolved_revision()— easy to misread as a registration failure rather than what it actually is: no catalog entry to fall back to.
Step 2: Register it with a backend
from mellea.backends.huggingface import LocalHFBackend
backend = LocalHFBackend(model_id="ibm-granite/granite-3.2-8b-instruct")
backend.add_adapter(custom_failure_check_adapter()) # downloads and loads the weights
add_adapter refuses a second registration under the same qualified name, so
guard repeated calls (e.g. inside a helper function) the same way the
built-in adapters do:
qualified_name = "custom-failure-check_alora"
if qualified_name not in backend.list_adapters():
backend.add_adapter(custom_failure_check_adapter())
Step 3: Invoke it
There are two ways to invoke a registered adapter, depending on whether you need raw output or a pass/fail validation.