You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
LocalFileBinding is a stub from issue 0.1, raising NotImplementedError on each verb. Reality A (today's IntrinsicAdapter — local PEFT/aLoRA file) needs the four verbs working before the binding is usable.
Agreed design
Four verbs
prepare() — resolves configured HF revision (from catalogue pinned SHA; defaults to the pinned SHA if unspecified), downloads PEFT weights via the existing HF path, registers with AdapterMixin.add_adapter, then loads with AdapterMixin.load_peft_adapter.
activate() — switches adapter on (PEFT layer enabled) via the backend's PEFT activation primitive.
deactivate() — switches off. Auto-called by adapter_scope even on exception.
release() — removes PEFT adapter via AdapterMixin.unload_peft_adapter. Second call is a no-op.
Session-scoped: prepare once per session. Call-scoped: activate/deactivate.
Corrected 2026-08-11. This line previously read "prepare once per session (or
explicit release()+prepare() cycle)". That contradicts the merged WeightsBinding
ABC from #1134, which states: "release() — terminal; releases all resources. The
binding is not reusable after release()." The implementation follows the ABC, so the
cycle this issue promised is not available. Which of the two is right is an open
question, tracked in #1528 — that issue also covers the related defect that release() leaves the adapter registered in the backend's _added_adapters, so its qualified_name stays claimed for the backend's lifetime.
from_catalog() classmethod
LocalFileBinding.from_catalog(name: str) -> LocalFileBinding — looks up the catalogue entry by name (post-0.2, with pinned revision), returns a fully configured binding. This is the user-facing standard path:
Superseded. Do not implement from this section. The span work moved to #1466;
see "Criteria moved out" below for why. The names originally written here
(intrinsic.call, intrinsic.prepare, intrinsic.activate, intrinsic.deactivate, intrinsic.parse) were never the convention this repo uses and must not be
copied. The merged naming, established by #1140 / PR #1422 and documented in docs/dev/adapter_observability.md, is adapter_function for the parent and adapter_function.<phase> for the children, with mellea.adapter_function.*
span attributes. Likewise the content-capture gate is MELLEA_TRACES_CONTENT
(plural TRACES), not MELLEA_TRACE_CONTENT — the latter never existed; see docs/dev/adapter_observability.md:133, which already records this same mistake
against #1140's criteria.
Metric hooks (ship with this PR)
ADAPTER_FUNCTION_PHASE_COMPLETE and ADAPTER_FUNCTION_INVOCATION_COMPLETE are fired
from the lifecycle, and AdapterFunctionMetricsPlugin (already merged, at mellea/telemetry/metrics_plugins.py) turns them into metrics. Firing the hooks is
correct and stays in scope here; only span production moved to #1466.
docs/docs/advanced/intrinsics.md — add new Adapter(weights=LocalFileBinding.from_catalog(...)) construction pattern; old IntrinsicAdapter(...) shown as deprecated with migration note
docs/examples/intrinsics/ — at least one example shows new construction; existing examples gain model_options=
Both user-facing docs items deferred, 2026-08-11. Rationale, recorded here rather
than left as a silently unmet criterion: this PR makes LocalFileBinding and adapter_scope real, but nothing in production calls them — _generate_from_intrinsic
still generates outside the scope, and resolve_adapter still returns adapters whose io_contract raises. Documenting Adapter(weights=LocalFileBinding.from_catalog(...))
as a construction pattern now would be documenting a path a user cannot yet use, and
deprecating IntrinsicAdapter(...) in favour of it would be actively wrong. Both belong
with the cutover that makes the path real: #1465 for the generation path, #1144 for the
shim removal and tutorial rewrite.
Out of scope
EmbeddedBinding (2.3), ServerMediatedBinding (3.1), long-running session refresh policy (deferred — PR #1080 §17 Q5), full rewrite of docs/dev/intrinsics_and_adapters.md (4.1).
Also out of scope, added 2026-07-29: routing _generate_from_intrinsic through adapter_scope (#1465) and emitting the adapter-function spans from a tracing plugin (#1466). See "Criteria moved out" below.
Acceptance criteria
All four verbs implemented for LocalFileBinding
prepare() uses pinned revision from catalogue; revision="main" opts into tracking-latest
activate/deactivate toggle PEFT layer correctly
release() unloads the weights and is idempotent on a second call
LocalFileBinding.from_catalog("answerability") returns correct binding with pinned revision
AdapterFunctionMetricsPlugin: mellea.adapter_function.invocations counter increments; mellea.adapter_function.parse_failures on AdapterSchemaMismatchError; mellea.adapter_function.phase_duration records prepare + activate durations (the parse_failures half needed a fix: adapter_scope declared outcome: Literal["success", "error"] and so could never emit "schema_error",
the only value that increments that counter. It now classifies AdapterSchemaMismatchError separately.)
PR #1454 removes the inline span implementation, because spans must be produced
by a plugin rather than opened inside mellea/backends/ — see the PR discussion
for the reasoning. These were acceptance criteria here and are now tracked where
the work will actually happen, rather than left unticked on this issue:
Criterion text below is restated in the merged naming convention
(adapter_function / MELLEA_TRACES_CONTENT), not in the intrinsic.* / MELLEA_TRACE_CONTENT spellings this issue was originally written with — neither of
those ever existed in the codebase.
The metrics criterion is unaffected and still applies — firing the hooks is
correct, and prepare() is reachable through from_catalog(). The last of the
four moved because generation does not yet run inside adapter_scope; that
cutover is #1465, and it is not "4.1" (#1144, which is shim removal plus a
docs rewrite).
Test plan
Unit tests (test/backends/test_adapters/test_local_file_binding.py) with mocked HF download + mocked backend:
Parent epic: #929
Design proposal: PR #1080 §8.1, §9.2, §9.3
Phase: 2 (Wave 4 — start after 2.1 merges)
Depends on: #1134, #1135, #1140
Blocks: #1144, #385, #1465
Problem
LocalFileBindingis a stub from issue 0.1, raisingNotImplementedErroron each verb. Reality A (today'sIntrinsicAdapter— local PEFT/aLoRA file) needs the four verbs working before the binding is usable.Agreed design
Four verbs
prepare()— resolves configured HF revision (from catalogue pinned SHA; defaults to the pinned SHA if unspecified), downloads PEFT weights via the existing HF path, registers withAdapterMixin.add_adapter, then loads withAdapterMixin.load_peft_adapter.activate()— switches adapter on (PEFT layer enabled) via the backend's PEFT activation primitive.deactivate()— switches off. Auto-called byadapter_scopeeven on exception.release()— removes PEFT adapter viaAdapterMixin.unload_peft_adapter. Second call is a no-op.Session-scoped:
prepareonce per session. Call-scoped:activate/deactivate.from_catalog()classmethodLocalFileBinding.from_catalog(name: str) -> LocalFileBinding— looks up the catalogue entry by name (post-0.2, with pinned revision), returns a fully configured binding. This is the user-facing standard path:OTel spans— moved to #1466 (2026-07-29)Metric hooks (ship with this PR)
ADAPTER_FUNCTION_PHASE_COMPLETEandADAPTER_FUNCTION_INVOCATION_COMPLETEare firedfrom the lifecycle, and
AdapterFunctionMetricsPlugin(already merged, atmellea/telemetry/metrics_plugins.py) turns them into metrics. Firing the hooks iscorrect and stays in scope here; only span production moved to #1466.
Docs updates
docs/dev/adapter_observability.md(from 2.1) — cover the LocalFile lifecycle andwhich metric hooks it fires. Not span attributes: those moved to feat(telemetry): emit adapter-function spans from a tracing plugin (Epic #929 Phase 2) #1466 with the
spans themselves.
docs/docs/advanced/intrinsics.md— add newAdapter(weights=LocalFileBinding.from_catalog(...))construction pattern; oldIntrinsicAdapter(...)shown as deprecated with migration notedocs/examples/intrinsics/— at least one example shows new construction; existing examples gainmodel_options=Both user-facing docs items deferred, 2026-08-11. Rationale, recorded here rather
than left as a silently unmet criterion: this PR makes
LocalFileBindingandadapter_scopereal, but nothing in production calls them —_generate_from_intrinsicstill generates outside the scope, and
resolve_adapterstill returns adapters whoseio_contractraises. DocumentingAdapter(weights=LocalFileBinding.from_catalog(...))as a construction pattern now would be documenting a path a user cannot yet use, and
deprecating
IntrinsicAdapter(...)in favour of it would be actively wrong. Both belongwith the cutover that makes the path real: #1465 for the generation path, #1144 for the
shim removal and tutorial rewrite.
Out of scope
EmbeddedBinding(2.3),ServerMediatedBinding(3.1), long-running session refresh policy (deferred — PR #1080 §17 Q5), full rewrite ofdocs/dev/intrinsics_and_adapters.md(4.1).Also out of scope, added 2026-07-29: routing
_generate_from_intrinsicthroughadapter_scope(#1465) and emitting the adapter-function spans from a tracing plugin (#1466). See "Criteria moved out" below.Acceptance criteria
LocalFileBindingprepare()uses pinned revision from catalogue;revision="main"opts into tracking-latestactivate/deactivatetoggle PEFT layer correctlyrelease()unloads the weights and is idempotent on a second call— not met, deferred to refactor(backends): release() leaves the adapter registered; is a binding reusable after release? (Epic #929) #1528.release()cleanly unregistersrelease()calls
unload_peft_adapter, which removes the entry from the backend's loadedset but not from
_added_adapters, the registered set. There is no inverse verbfor
add_adapterto call. PR feat(backends): LocalFileBinding implements verbs (PEFT/aLoRA path) + from_catalog() (Epic #929 Phase 2) #1454 correctsrelease()'s docstring to state whatit does and does not release, and makes the resulting "Backend refused to register"
error name both possible causes; whether a released name should become re-claimable
at all is the open question on refactor(backends): release() leaves the adapter registered; is a binding reusable after release? (Epic #929) #1528.
LocalFileBinding.from_catalog("answerability")returns correct binding with pinned revisionAdapterFunctionMetricsPlugin:mellea.adapter_function.invocationscounter increments;mellea.adapter_function.parse_failuresonAdapterSchemaMismatchError;mellea.adapter_function.phase_durationrecords prepare + activate durations(the
parse_failureshalf needed a fix:adapter_scopedeclaredoutcome: Literal["success", "error"]and so could never emit"schema_error",the only value that increments that counter. It now classifies
AdapterSchemaMismatchErrorseparately.)— deferred to refactor(backends): route intrinsic generation through adapter_scope (Epic #929 Phase 2) #1465 / refactor(intrinsics): remove deprecation shims; rewrite intrinsics_and_adapters.md; write 3 tutorials (Epic #929 Phase 4) #1144, see "Docs updates"docs/docs/advanced/intrinsics.mdupdated: new construction pattern present, deprecated old pattern noted— deferred to refactor(backends): route intrinsic generation through adapter_scope (Epic #929 Phase 2) #1465 / refactor(intrinsics): remove deprecation shims; rewrite intrinsics_and_adapters.md; write 3 tutorials (Epic #929 Phase 4) #1144, see "Docs updates"docs/examples/intrinsics/updated; all examples passuv run pytest docs/examples/intrinsics/docs/dev/adapter_observability.mdupdated for the LocalFile lifecycle and metric hooks (span-attribute sections moved to feat(telemetry): emit adapter-function spans from a tracing plugin (Epic #929 Phase 2) #1466)ruff format,ruff check,mypycleanCriteria moved out (2026-07-29)
PR #1454 removes the inline span implementation, because spans must be produced
by a plugin rather than opened inside
mellea/backends/— see thePR discussion
for the reasoning. These were acceptance criteria here and are now tracked where
the work will actually happen, rather than left unticked on this issue:
Criterion text below is restated in the merged naming convention
(
adapter_function/MELLEA_TRACES_CONTENT), not in theintrinsic.*/MELLEA_TRACE_CONTENTspellings this issue was originally written with — neither ofthose ever existed in the codebase.
adapter_functionparent span emitted; child spansadapter_function.prepare,adapter_function.activate,adapter_function.deactivate,adapter_function.parsewith required attributesadapter_function.preparerecords resolved HF SHA (not"main") asmellea.adapter_function.revisionMELLEA_TRACES_CONTENT=1: content events present; absent otherwiseThe metrics criterion is unaffected and still applies — firing the hooks is
correct, and
prepare()is reachable throughfrom_catalog(). The last of thefour moved because generation does not yet run inside
adapter_scope; thatcutover is #1465, and it is not "4.1" (#1144, which is shim removal plus a
docs rewrite).
Test plan
Unit tests (
test/backends/test_adapters/test_local_file_binding.py) with mocked HF download + mocked backend:test_prepare_uses_pinned_revisiontest_prepare_allows_main_overridetest_release_is_idempotenttest_from_catalog_returns_binding_with_correct_revisiontest_activate_deactivate_call_correct_mixin_verbsSpan tests via synthetic OTel exporter:— moved to feat(telemetry): emit adapter-function spans from a tracing plugin (Epic #929 Phase 2) #1466 with the span criteriatest_call_span_emitted,test_prepare_span_has_revision_attribute,test_content_events_absent_by_default,test_content_events_present_with_gate_settest_metrics_invocation_counter_incrementstest_metrics_parse_failures_increments— inject synthetic schema-mismatchIntegration tests (
test/backends/test_adapters/test_local_file_integration.py,@pytest.mark.integration,@pytest.mark.hf,@pytest.mark.slow):LocalHFBackend × LocalFileBinding × {lora, alora} × {check_answerability, requirement_check}— full lifecycle{"requirement_likelihood": 0.9}raisesAdapterSchemaMismatchErrorQualitative (optional,
@pytest.mark.qualitative):test_check_answerability_qualityBreaking changes
None for end users. Replaces internal stubs; behaviour matches existing
IntrinsicAdapterruntime path post-1.A.References
MELLEA_TRACES_CONTENTcontent-capture gate conventionadapter_functionspan and metric naming convention;AdapterFunctionMetricsPlugindocs/dev/adapter_observability.md— the merged naming of record