mellea.backends.adapters.adapter
Adapter classes for adding fine-tuned modules to inference backends.
The primary public surface is :func:AdapterMixin.resolve_adapter (find or lazily
register an adapter by capability name) and :meth:AdapterMixin._find_adapter
(look up a registered adapter). :class:AdapterMixin is mixed into backends that
support runtime adapter loading and unloading.
LocalHFAdapter, IntrinsicAdapter, and EmbeddedIntrinsicAdapter are
deprecation shims retained for backwards compatibility. They satisfy
isinstance(x, _core.Adapter) but delegate all behaviour to the new dataclass.
get_adapter_for_intrinsic is similarly deprecated; prefer resolve_adapter.
Functions
FUNC get_adapter_for_intrinsic
get_adapter_for_intrinsic(intrinsic_name: str, intrinsic_adapter_types: list[AdapterType] | tuple[AdapterType, ...], available_adapters: dict[str, T]) -> T | None
Find an adapter from a dict of available adapters based on the adapter function name and its allowed adapter types.
Args:
intrinsic_name: The name of the adapter function, e.g."answerability".intrinsic_adapter_types: The adapter types allowed for this adapter function, e.g.[AdapterType.ALORA, AdapterType.LORA].available_adapters: The available adapters to choose from; mapsadapter.qualified_nameto the adapter object.
Returns:
- T | None: The first matching adapter found, or
Noneif no match exists.
Classes
CLASS Adapter
An adapter that can be added to a single backend.
An adapter can only be registered with one backend at a time. Use
adapter.qualified_name when referencing the adapter after adding it.
Args:
name: Human-readable name of the adapter.adapter_type: Enum describing the adapter type (e.g.AdapterType.LORAorAdapterType.ALORA).
Attributes:
qualified_name: Unique name used for loading and lookup; formed as"<name>_<adapter_type.value>".backend: The backend this adapter has been added to, orNoneif not yet added.path: Filesystem path to the adapter weights; set when the adapter is added to a backend.
CLASS LocalHFAdapter
Abstract adapter subclass for locally loaded Hugging Face model backends.
Subclasses must implement get_local_hf_path to return the filesystem path
from which adapter weights should be loaded given a base model name.
Methods:
FUNC get_local_hf_path
get_local_hf_path(self, base_model_name: str) -> str
Return the local filesystem path from which adapter weights should be loaded.
Args:
base_model_name: The base model name; typically the last component of the Hugging Face model ID (e.g."granite-4.0-micro").
Returns:
- Filesystem path to the adapter weights directory.
CLASS IntrinsicAdapter
Deprecated shim for adapters that implement adapter functions.
.. deprecated::
Use :class:~mellea.backends.adapters.Adapter directly.
IntrinsicAdapter will be removed in a future release (Epic #929,
issue #1144).
Subtype of :class:Adapter for models that:
- implement adapter functions
- are packaged as LoRA or aLoRA adapters on top of a base model
- use the shared model loading code in
mellea.formatters.granite.intrinsics - use the shared input and output processing code in
mellea.formatters.granite.intrinsics
Args:
intrinsic_name: Name of the adapter function (e.g."answerability"); the adapter'squalified_namewill be derived from this.adapter_type: Enum describing the adapter type; defaults toAdapterType.ALORA.config_file: Path to a YAML config file defining the adapter function's I/O transformations; mutually exclusive withconfig_dict.config_dict: Dict defining the adapter function's I/O transformations; mutually exclusive withconfig_file.base_model_name: Base model name used to look up the I/O processing config when neitherconfig_filenorconfig_dictare provided.
Attributes:
intrinsic_name: Name of the adapter function this adapter implements.intrinsic_metadata: Catalog metadata for the adapter function.base_model_name: Base model name provided at construction, if any.adapter_type: The adapter type (LORAorALORA).config: Parsed I/O transformation configuration for the adapter function.
.. note::
identity, io_contract, and weights are Phase 1 internal scaffolding
populated in __init__ to satisfy the new :class:~mellea.backends.adapters.Adapter
protocol. They are not meaningful consumer-facing attributes; io_contract and
weights raise :exc:NotImplementedError and will be replaced in Phase 2
(issues #1137, #1141).
Methods:
FUNC get_local_hf_path
get_local_hf_path(self, base_model_name: str) -> str
Return the local filesystem path from which adapter weights should be loaded.
Downloads the adapter weights if they are not already cached locally.
Args:
base_model_name: The base model name; typically the last component of the Hugging Face model ID (e.g."granite-3.3-8b-instruct").
Returns:
- Filesystem path to the downloaded adapter weights directory.
FUNC download_and_get_path
download_and_get_path(self, base_model_name: str) -> str
Download the required adapter function files if necessary and return the path to them.
Args:
base_model_name: the base model; typically the last part of the Hugging Face model id like "granite-3.3-8b-instruct"
Returns:
- a path to the files
CLASS AdapterMixin
Mixin class for backends capable of utilizing adapters.
Three verbs are universal across every adapter reality (LocalFile/PEFT,
Embedded/Granite Switch, ServerMediated): base_model_name,
add_adapter, and list_adapters. The remaining four verbs are
reality-specific — a concrete backend overrides only the verb(s) matching
its own reality; the others keep raising NotImplementedError.
Attributes:
base_model_name: The short model name used to identify adapter variants (e.g."granite-3.3-8b-instruct"for"ibm-granite/granite-3.3-8b-instruct").
Methods:
FUNC base_model_name
base_model_name(self) -> str
Return the short model name used for adapter variant lookup.
Returns:
- The base model name (e.g.
"granite-3.3-8b-instruct").
FUNC add_adapter
add_adapter(self, adapter: AdapterInput) -> None
Register an adapter with this backend so it can be loaded later.
The adapter must not already have been added to a different backend.
Concrete backends accept the full AdapterInput union but raise
TypeError for adapter realities they do not implement (e.g. a PEFT
backend rejects an embedded adapter), so a statically valid call may
still be rejected at runtime.
Args:
adapter: The adapter to register with this backend.
Raises:
TypeError: Ifadapterbelongs to a reality this backend does not support.
FUNC list_adapters
list_adapters(self) -> list[str]
Return the qualified names of all adapters registered with this backend.
Returns:
- list[str]: Qualified adapter names for all adapters that have been
registered via
add_adapter.
FUNC load_peft_adapter
load_peft_adapter(self, adapter_qualified_name: str) -> None
Load a previously registered PEFT adapter into the underlying model.
LocalFile/PEFT reality only (e.g. a locally hosted Hugging Face
model). The adapter must have been registered via add_adapter
before calling this method.
Args:
adapter_qualified_name: Theadapter.qualified_nameof the adapter to load.
Raises:
NotImplementedError: If this backend's adapter reality is not LocalFile/PEFT.
FUNC unload_peft_adapter
unload_peft_adapter(self, adapter_qualified_name: str) -> None
Unload a previously loaded PEFT adapter from the underlying model.
LocalFile/PEFT reality only (e.g. a locally hosted Hugging Face model).
Args:
adapter_qualified_name: Theadapter.qualified_nameof the adapter to unload.
Raises:
NotImplementedError: If this backend's adapter reality is not LocalFile/PEFT.
FUNC activate_peft_adapter
activate_peft_adapter(self, adapter_qualified_name: str) -> None
Switch a previously loaded PEFT adapter on for subsequent generation.
LocalFile/PEFT reality only (e.g. a locally hosted Hugging Face
model). The adapter must have been loaded via load_peft_adapter
before calling this method.
Args:
adapter_qualified_name: Theadapter.qualified_nameof the adapter to activate.
Raises:
NotImplementedError: If this backend's adapter reality is not LocalFile/PEFT.
FUNC deactivate_peft_adapter
deactivate_peft_adapter(self, adapter_qualified_name: str) -> None
Switch off any active PEFT adapter so generation uses the base model.
LocalFile/PEFT reality only (e.g. a locally hosted Hugging Face model).
Args:
adapter_qualified_name: Theadapter.qualified_nameof the adapter to deactivate. Accepted for symmetry withactivate_peft_adapter; the underlying primitive clears all active PEFT adapters regardless of name.
Raises:
NotImplementedError: If this backend's adapter reality is not LocalFile/PEFT.
FUNC render_controls
render_controls(self, adapter_qualified_name: str, active: bool) -> None
Render or clear the control tokens for a baked-in embedded adapter.
Embedded/Granite Switch reality only. Weights are already baked into the model; this only toggles the control-token rendering that activates or deactivates the adapter's behaviour for subsequent requests.
Args:
adapter_qualified_name: Theadapter.qualified_nameof the adapter to activate or deactivate.active:Trueto render the adapter's control tokens,Falseto clear them.
Raises:
NotImplementedError: If this backend's adapter reality is not Embedded/Granite Switch.
FUNC set_request_adapter
set_request_adapter(self, adapter_qualified_name: str) -> None
Select the adapter to use for the next request.
ServerMediated reality only — for servers that accept an adapter selection per request rather than loading/unloading weights or toggling control tokens locally. No backend implements this reality yet.
Args:
adapter_qualified_name: Theadapter.qualified_nameof the adapter to select.
Raises:
NotImplementedError: Always — the ServerMediated adapter reality has no implementation yet.
FUNC resolve_adapter
resolve_adapter(self, name: str) -> _AdapterCore
Find or lazily register an adapter by capability name.
Default implementation preserves Phase 0 behaviour, using the internal
_added_adapters dict that concrete backends maintain. Override in
Phase 2 (see epic #929) to implement proper lifecycle management.
Args:
name: Capability name (e.g."answerability").
Returns:
- The registered adapter with the given capability.
Raises:
ValueError: If the backend has no model ID.KeyError: If the adapter cannot be found after registration.
FUNC adapter_scope
adapter_scope(self, adapter: '_AdapterCore | None')
Context manager wrapping adapter activation and deactivation.
A no-op when adapter is None. Otherwise: activates
adapter.weights, yields, then always deactivates — even if the with
body raises. Each phase fires ADAPTER_FUNCTION_PHASE_COMPLETE, and
ADAPTER_FUNCTION_INVOCATION_COMPLETE fires on the way out, carrying the
overall outcome.
This method fires hooks only; it does not open spans. Span production is a
plugin's job (see #1464 for the rule and #1466 for the adapter-function
spans), and the ADAPTER_FUNCTION_* family currently has no start hook for
a plugin to open a span on. See docs/dev/adapter_observability.md for the
metric schema.
deactivate() is guarded on activate()'s own side effect having
completed, not on the activate phase's hook dispatch also succeeding.
If a plugin subscribed to ADAPTER_FUNCTION_PHASE_COMPLETE raises after
activate() already flipped the adapter on, deactivate() still runs —
telemetry must not be able to strand the adapter active.
Not atomic across the whole scope: _adapter_activation_lock() is
held only inside each of activate()/deactivate()'s own verb calls
(see LocalFileBinding.activate), not for the with body in between.
Two concurrent adapter_scope() calls on one backend can therefore
interleave — one thread's body can run while a different adapter is
active, activated by another thread's call. Widening the lock to span
the whole scope was tried and reverted: it deadlocks the moment the
body does real async generation (confirmed against
test_local_file_e2e.py), because that work runs on the shared
event-loop thread while this thread holds the lock — a same-thread
RLock doesn't help across threads. No caller combines concurrent
adapter_scope() calls today (nothing outside tests calls it at all),
so this is latent, not reachable — but #1465 (wiring real generation
through this scope) has to solve the atomicity and the threading
interaction together, not layer one fix on top of the other.
Args:
adapter: The adapter to activate, orNone(no-op).
Raises:
BaseException: An error raised by activation, thewithbody, or deactivation. If both the body and deactivation fail, the body error remains primary and the deactivation error is chained.
CLASS EmbeddedIntrinsicAdapter
Deprecated shim for adapter functions embedded in a Granite Switch model.
.. deprecated::
Use :class:~mellea.backends.adapters.Adapter directly.
EmbeddedIntrinsicAdapter will be removed in a future release
(Epic #929, issue #1144).
Unlike PEFT-based adapters that are loaded into the model at runtime,
embedded adapters are already baked into the model weights and activated
via control tokens injected by the model's chat template. Only the I/O
transformation config (io.yaml) is needed; no adapter weights are
downloaded or loaded.
Args:
intrinsic_name: Name of the adapter function (e.g."answerability").config: Parsed I/O transformation configuration (fromio.yaml).technology: Adapter technology in the switch model —"lora"or"alora". Determines where the control token is placed in the chat template (beginning of sequence for LoRA, before generation prompt for aLoRA).
Attributes:
intrinsic_name: Name of the adapter function this adapter implements.config: Parsed I/O transformation configuration.technology:"lora"or"alora".
.. note::
identity, io_contract, and weights are Phase 1 internal scaffolding
populated in __init__ to satisfy the new :class:~mellea.backends.adapters.Adapter
protocol. They are not meaningful consumer-facing attributes; io_contract and
weights raise :exc:NotImplementedError and will be replaced in Phase 2
(issues #1137, #1142).
Methods:
FUNC from_model_directory
from_model_directory(model_path: str | pathlib.Path, intrinsic_name: str | None = None) -> list['EmbeddedIntrinsicAdapter']
Load embedded adapters from a Granite Switch model directory.
Reads adapter_index.json and the corresponding io_configs/*/io.yaml
files from the model directory.
Args:
model_path: Path to a Granite Switch model directory that containsadapter_index.jsonandio_configs/.intrinsic_name: If provided, only load the adapter matching this adapter function name.Noneloads all adapters.
Returns:
- list[EmbeddedIntrinsicAdapter]: One adapter per entry in the index.
Raises:
FileNotFoundError: Ifadapter_index.jsonis missing.ValueError: If anio.yamlfile listed in the index cannot be found or if no adapters are found.
FUNC from_hub
from_hub(repo_id: str, revision: str = 'main', cache_dir: str | None = None, intrinsic_name: str | None = None) -> list['EmbeddedIntrinsicAdapter']
Load embedded adapters from a Granite Switch model on Hugging Face Hub.
Downloads adapter_index.json and the io_configs/ directory, then
delegates to :meth:from_model_directory.
Args:
repo_id: Hugging Face Hub repository ID (e.g."ibm-granite/granite-switch-micro").revision: Git revision to download from.cache_dir: Local cache directory;Nonefor the default.intrinsic_name: If provided, only load the adapter matching this adapter function name.Noneloads all adapters.
Returns:
- list[EmbeddedIntrinsicAdapter]: One adapter per entry in the index.
Raises:
ImportError: Ifhuggingface_hubis not installed.PermissionError: If the repository is private or gated and the current Hugging Face credentials do not grant access.FileNotFoundError: If the downloaded snapshot has noadapter_index.json(wrong repo/revision, not a Granite Switch model, or a stale cache).ValueError: If no adapters are found (delegated from :meth:from_model_directory).
FUNC from_source
from_source(source: str, revision: str = 'main', cache_dir: str | None = None, intrinsic_name: str | None = None) -> list['EmbeddedIntrinsicAdapter']
Load embedded adapters from a local directory or Hugging Face Hub.
Automatically detects whether source is a local filesystem path
or a Hugging Face Hub repo ID, and delegates accordingly.
Args:
source: Local path to a model directory, or a Hugging Face Hub repo ID (e.g."ibm-granite/granite-switch-micro").revision: Git revision (only used for Hub downloads).cache_dir: Cache directory (only used for Hub downloads).intrinsic_name: If provided, only load the adapter matching this adapter function name.Noneloads all adapters.
Returns:
- list[EmbeddedIntrinsicAdapter]: One adapter per entry in the index.
CLASS CustomIntrinsicAdapter
Deprecated shim for user-defined custom adapter functions.
.. deprecated::
Use :class:~mellea.backends.adapters.Adapter directly.
CustomIntrinsicAdapter will be removed in a future release
(Epic #929, issue #1144).
This class has the same functionality as IntrinsicAdapter, except that
its constructor monkey-patches Mellea global variables to enable the backend
to load the user's adapter.
Args:
model_id: The Hugging Face model ID used for downloading model weights; expected format is"<user-id>/<repo-name>".intrinsic_name: Catalog name for the adapter function; defaults to the repository name portion ofmodel_idif not provided.base_model_name: The short name of the base model (NOT its repo ID).