Repository navigation
refactor(backends): AdapterMixin verb rename/narrow + resolve_model_options + IntrinsicMetricsPlugin (Epic #929 Phase 2) #1140
Description
Parent epic: #929
Design proposal: PR #1080 §13, §16 Phase 2
Phase: 2 (Wave 3 — start after 1.A merges, in parallel with 1.B/1.C/1.D)
Depends on: 1.A
Blocks: 2.2, 2.3
Problem
Two related backend-surface concerns that touch the same files:
(a) Mixin verb mismatch. AdapterMixin (verified main mellea/backends/adapters/adapter.py:240) exposes five verbs: base_model_name, add_adapter, load_adapter, unload_adapter, list_adapters. The proposed design uses different verbs: load_peft_adapter, unload_peft_adapter, render_controls, set_request_adapter. This is a rename + introduce, not a pure rename.
(b) Per-call option merging. Each backend calls _simplify_and_merge on every adapter call. Duplicated logic and a known precedence bug source (PR #972 was a fix for this).
Agreed design
(a) AdapterMixin verb rename/narrow
AdapterMixin after this issue:
AdapterMixin:
load_peft_adapter / unload_peft_adapter # LocalFile reality
render_controls # Embedded reality
set_request_adapter # ServerMediated reality
Map existing implementations onto new verbs case-by-case. base_model_name and list_adapters: remove if unused after 1.A; otherwise relocate with documented justification. Bindings (2.2/2.3) call into these from their prepare/activate/etc.
Backends affected: LocalHFBackend, OpenAIBackend (the two adapter-supporting backends). Not affected: OllamaBackend, WatsonxBackend, LiteLLMBackend.
(b) Option resolution
New mellea/backends/_options.py:resolve_model_options — documents and implements precedence: caller-passed model_options= > helper defaults > backend defaults. Both adapter-supporting backends replace _simplify_and_merge with resolve_model_options on the adapter call path.
IntrinsicMetricsPlugin
New plugin at mellea/core/plugins/intrinsic_metrics.py. Three OTel metrics:
mellea.intrinsic.invocations— counter; labels:name,revision,binding_type,adapter_type,outcome(success/schema_error/error)mellea.intrinsic.phase_duration_ms— histogram; labels:name,phase(prepare/activate/generate/parse/deactivate)mellea.intrinsic.parse_failures— counter; labels:name,revision. Schema-drift detector: a climbing counter against(name, revision)means a breaking schema change was pushed upstream. Increments on eachAdapterSchemaMismatchError.
Auto-wired via the existing TokenMetricsPlugin registration pattern.
Observability doc
New docs/dev/adapter_observability.md — span tree structure, metric labels, parse_failures schema-drift detector pattern, MELLEA_TRACE_CONTENT content-capture gate. Phases 2.2/2.3 add span emission details; this issue writes the structure.
Out of scope
Binding implementations (2.2, 2.3), span instrumentation (lives where verbs are implemented), HF embedded adapter support (#1018).
Acceptance criteria
-
AdapterMixinexposes exactly the four verbs above; old verbs removed or relocated with documented justification -
LocalHFBackendandOpenAIBackendimplement the new verb set -
resolve_model_optionsexists, documented, tested - Both adapter-supporting backends use
resolve_model_optionsinstead of_simplify_and_mergeon adapter call path -
IntrinsicMetricsPluginregisterable like existing plugins - All three metrics registered with correct label sets
-
mellea.intrinsic.parse_failuresincrements onAdapterSchemaMismatchError -
docs/dev/adapter_observability.mdwritten; covers span tree, metric labels,parse_failurespattern,MELLEA_TRACE_CONTENTgate; markdownlint passes - Existing tests pass (precedence behaviour unchanged)
- New unit tests cover precedence: caller > helper > backend
- Test asserts
AdapterMixinexposes exactly the four verbs (catches accidental re-additions) - Unit tests for
IntrinsicMetricsPluginusing synthetic OTel exporter -
ruff format,ruff check,mypyclean
Test plan
- Existing backend tests pass
resolve_model_optionsprecedence tests: each pair (caller-vs-helper, caller-vs-backend, helper-vs-backend)- Verb set test: assert
AdapterMixinhas exactly the four verbs IntrinsicMetricsPluginunit tests:test_invocations_counter_emits_on_successtest_invocations_counter_emits_schema_error_outcometest_parse_failures_counter_incrementstest_phase_duration_histogram_emits_for_each_phase
Breaking changes
AdapterMixinverb rename — downstream backends extendingAdapterMixinmust update. Migration table (old → new verb names) in changelog._simplify_and_mergeremoval from adapter call path — internal API only.
References
- PR docs: design proposal for adapter lifecycle (#929) — review channel, do not merge #1080 §13, §16 Phase 2
- PR fix: model options with intrinsics #972 — historic precedence bug (evidence for centralisation)
- PR feat: add embedded adapters (granite switch) to openai backend #881 — Reality B added to OpenAI backend; uses what becomes
render_controls - add support for granite-switch / embedded adapters on our huggingface backend #1018 — HF embedded adapters; will use
render_controlspost-2.1
Activity
planetf1 commentedon Jun 11, 2026
This issue uses vocabulary that has been superseded by the agreed Epic #929 glossary. Please use the following terms in any new work related to this issue:
The canonical definitions are now in the project glossary and were established as part of #1192. Python symbol names (
IntrinsicAdapter,fetch_intrinsic_metadata, etc.) keep their current names until Phase 1 (#1136) and Phase 4 (#1144).feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
feat(intrinsics): shim old adapters onto Adapter dataclass; rewrite c…
7 remaining items
planetf1 commentedon Jul 21, 2026
Update: implementation is up in #1422 (draft). Two corrections to my earlier comment on this issue, now that Phase 1 (#1269) has merged underneath it:
Verb count is 7, not 4. My earlier comment's rationale cited
call_intrinsic's oldlist_adapters()/add_adapter()dispatch logic — that no longer exists; Phase 1 replaced it withresolve_adapter(). This issue's acceptance criteria state "exposes exactly the four verbs" three times, but that's unsatisfiable now:resolve_adapter()depends onbase_model_nameandadd_adapterstaying on the mixin, alongside the four reality-specific verbs (load_peft_adapter/unload_peft_adapter,render_controls,set_request_adapter) pluslist_adapters. So the actual shipped count is 7 universal+reality-specific verbs, not 4. Flagging this plainly rather than letting the checklist silently mismatch the code — happy to adjust the AC text if a maintainer prefers.Env var typo: this issue (and #1141/#1142) reference
MELLEA_TRACE_CONTENT— the actual variable isMELLEA_TRACES_CONTENT. Worth fixing at the source since #1141/#1142 are both blocked on this issue anyway.Full verb contract,
resolve_model_optionsprecedence, and theIntrinsicMetricsPluginskeleton's metrics are documented indocs/dev/adapter_observability.md(added in #1422).refactor(backends)!: narrow AdapterMixin verbs, centralize option res…
docs(adapter): document span tree and MELLEA_TRACE_CONTENT gate
test(telemetry): assert intrinsic metrics emit via in-memory OTel exp…
docs(adapter): align span + content-capture sections with existing co…
refactor(telemetry): name adapter-function metrics per the glossary
refactor(backends)!: narrow AdapterMixin verbs, centralize option res…