Parent epic: #929
Design proposal: PR #1080 §8.1, §9.2, §9.3
Phase: 2 (Wave 4 — start after 2.1 merges)
Depends on: 0.1, 0.2, 2.1
Blocks: 4.1
Problem
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 0.2 catalogue pinned SHA; defaults to pinned SHA if unspecified). Downloads PEFT weights via existing HF download path. Registers with backend via AdapterMixin.load_peft_adapter.
activate(ctx) — switches adapter on (PEFT layer enabled) via backend's existing PEFT activation primitives.
deactivate(ctx) — 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 (or explicit release()+prepare() cycle). Call-scoped: activate/deactivate.
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:
Adapter(name="answerability", weights=LocalFileBinding.from_catalog("answerability"))
OTel spans
Parent span intrinsic.call (wrapping adapter_scope from 1.A). Child spans:
intrinsic.prepare — attributes: intrinsic.name, intrinsic.revision (resolved SHA, not "main"), intrinsic.binding_type="local_file", intrinsic.source, download duration
intrinsic.activate — attribute: intrinsic.peft_name
intrinsic.deactivate
intrinsic.parse (emitted by io_contract.parse()) — attributes: intrinsic.revision, intrinsic.parse_ok, intrinsic.raw_len
Content capture (gated on MELLEA_TRACE_CONTENT env var, consistent with #1035/PR #1036): intrinsic.input.kwargs, intrinsic.output.raw, intrinsic.output.parsed as span events.
Docs updates (ship with this PR)
docs/dev/adapter_observability.md (from 2.1) — add LocalFile-specific span attributes
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=
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).
Acceptance criteria
Test plan
Unit tests (test/backends/adapters/test_local_file_binding.py) with mocked HF download + mocked backend:
test_prepare_uses_pinned_revision
test_prepare_allows_main_override
test_release_is_idempotent
test_from_catalog_returns_binding_with_correct_revision
test_activate_deactivate_call_correct_mixin_verbs
- Span tests via synthetic OTel exporter:
test_call_span_emitted, test_prepare_span_has_revision_attribute, test_content_events_absent_by_default, test_content_events_present_with_gate_set
test_metrics_invocation_counter_increments
test_metrics_parse_failures_increments — inject synthetic schema-mismatch
Integration tests (test/backends/adapters/test_local_file_integration.py, @pytest.mark.integration, @pytest.mark.hf, @pytest.mark.slow):
Qualitative (optional, @pytest.mark.qualitative): test_check_answerability_quality
Breaking changes
None for end users. Replaces internal stubs; behaviour matches existing IntrinsicAdapter runtime path post-1.A.
References
Parent epic: #929
Design proposal: PR #1080 §8.1, §9.2, §9.3
Phase: 2 (Wave 4 — start after 2.1 merges)
Depends on: 0.1, 0.2, 2.1
Blocks: 4.1
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 0.2 catalogue pinned SHA; defaults to pinned SHA if unspecified). Downloads PEFT weights via existing HF download path. Registers with backend viaAdapterMixin.load_peft_adapter.activate(ctx)— switches adapter on (PEFT layer enabled) via backend's existing PEFT activation primitives.deactivate(ctx)— 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 (or explicitrelease()+prepare()cycle). 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
Parent span
intrinsic.call(wrappingadapter_scopefrom 1.A). Child spans:intrinsic.prepare— attributes:intrinsic.name,intrinsic.revision(resolved SHA, not"main"),intrinsic.binding_type="local_file",intrinsic.source, download durationintrinsic.activate— attribute:intrinsic.peft_nameintrinsic.deactivateintrinsic.parse(emitted byio_contract.parse()) — attributes:intrinsic.revision,intrinsic.parse_ok,intrinsic.raw_lenContent capture (gated on
MELLEA_TRACE_CONTENTenv var, consistent with #1035/PR #1036):intrinsic.input.kwargs,intrinsic.output.raw,intrinsic.output.parsedas span events.Docs updates (ship with this PR)
docs/dev/adapter_observability.md(from 2.1) — add LocalFile-specific span attributesdocs/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=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).Acceptance criteria
LocalFileBindingprepare()uses pinned revision from catalogue;revision="main"opts into tracking-latestactivate/deactivatetoggle PEFT layer correctlyrelease()cleanly unregisters; second call is a no-opLocalFileBinding.from_catalog("answerability")returns correct binding with pinned revisionintrinsic.callparent span emitted; child spansintrinsic.prepare,intrinsic.activate,intrinsic.deactivate,intrinsic.parseemitted with required attributesintrinsic.preparerecords resolved HF SHA (not"main") asintrinsic.revisionMELLEA_TRACE_CONTENT=1: content events present; absent otherwiseIntrinsicMetricsPlugin:invocationscounter increments;parse_failuresonAdapterSchemaMismatchError;phase_duration_msrecords prepare + activate durationsdocs/docs/advanced/intrinsics.mdupdated: new construction pattern present, deprecated old pattern noteddocs/examples/intrinsics/updated; all examples passuv run pytest docs/examples/intrinsics/docs/dev/adapter_observability.mdupdated with LocalFile-specific attributesruff format,ruff check,mypycleanTest plan
Unit tests (
test/backends/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_verbstest_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/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_TRACE_CONTENTcontent-capture gate convention