Skip to content

refactor(intrinsics): remove deprecation shims; rewrite intrinsics_and_adapters.md; write 3 tutorials (Epic #929 Phase 4) #1144

Description

@planetf1

Parent epic: #929
Design proposal: PR #1080 §16 Phase 4, Part I §5 Q4
Phase: 4 (final)
Depends on: #1141, #1142, #1516, #1465, #1018 — all merged. Deprecation window opened 2026-09-02 (last dependency, #1018/PR #1593); team has decided to proceed now.


Update (2026-09-03) — scope correction: this is not just a deletion

The original scope below assumed callers were "already on new types" and this issue was cleanup. That's false as of 2026-09-03, verified against current main. Neither LocalHFBackend.add_adapter nor OpenAIBackend.add_adapter accepts a composed Adapter (_core.Adapter) directly — both reject it via isinstance gates and raise TypeError. The only working path today for LoRA/embedded intrinsic use is through the three deprecated shim classes (IntrinsicAdapter, EmbeddedIntrinsicAdapter, CustomIntrinsicAdapter), and that's true of production code, not just docs/examples:

  • AdapterMixin.resolve_adapter() (mellea/backends/adapters/adapter.py:612-724) — the shared lazy-registration path every backend inherits — directly constructs EmbeddedIntrinsicAdapter.from_source(...) (L675) or IntrinsicAdapter(name, adapter_type=AdapterType.LORA, base_model_name=base) (L688).
  • mellea/backends/huggingface.py (LocalHFBackend): add_adapter() (L2375, isinstance gate L2397-2403), register_embedded_adapter_model() (L2442, L2465), load_peft_adapter() (L2493 guard), _generate_from_intrinsic() (isinstance dispatch L891, L932, L1012).
  • mellea/backends/openai.py (OpenAIBackend): add_adapter() (L310, isinstance gate L327 — EmbeddedIntrinsicAdapter is the only accepted reality), register_embedded_adapter_model() (L361, L379), _generate_from_intrinsic() (gates at L760, L784, L884/890), self._added_adapters: dict[str, EmbeddedIntrinsicAdapter] type annotation (L278).
  • Product-facing, easy to miss: cli/alora/README_TEMPLATE.jinja (L44, L52) — every m alora upload --intrinsic invocation generates user scaffolding that subclasses CustomIntrinsicAdapter. This has to change in lockstep with the backend code, not as an afterthought.

Given this, the work splits into two PRs, stacked:

PR1 — build the composed-Adapter-direct path (additive, land first)

Goal: both backends accept a composed Adapter(identity, io_contract, weights_binding) directly, alongside the existing shim path (which keeps working unchanged — no behaviour change for existing callers in this PR).

Code:

  • LocalHFBackend.add_adapter / OpenAIBackend.add_adapter — extend the isinstance gates to accept a composed Adapter instance and dispatch correctly (LocalFileBinding → PEFT path, EmbeddedBinding → embedded path).
  • AdapterMixin.resolve_adapter() — construct a composed Adapter instead of the shim classes when registering. (This is the one call site both backends inherit — get this right once.)
  • register_embedded_adapter_model(), load_peft_adapter() (HF only), _generate_from_intrinsic() in both backends — same treatment, dispatch on binding type rather than the shim's own type.
  • cli/alora/README_TEMPLATE.jinja — generate composed-Adapter scaffolding instead of a CustomIntrinsicAdapter subclass.

Tests (new — none of this exists today, so none of it is covered today):

  • Unit tests for the new isinstance/dispatch branches in both backends.
  • Integration test exercising the composed path end-to-end through call_intrinsic/Intrinsic.
  • A real-GPU e2e case for the composed path, matching the pattern test: intrinsic HF generation (adapter_scope routing) has no real-model e2e coverage #1574/PR test(backends): add real-model GPU e2e for intrinsic adapter_scope routing #1608 established (public API, real model, asserts on hook payload + activation state, not generated content).
  • Rewrite the five test files that currently exercise production code through the shim classes to also/instead cover the composed path: test/backends/test_adapters/test_embedded_integration.py, test/backends/test_openai_intrinsics_unit.py, test/backends/test_huggingface_unit.py, test/backends/test_huggingface.py, and the TestOpenAIBackendRegistration class in test/backends/test_adapters/test_embedded_adapter.py.

Docs/examples (8 files):

  • docs/docs/advanced/intrinsics.md — rewrite the "Composable adapter construction (advanced)" section (currently accurately states neither backend supports this — flip it to the real, working example) and the "Direct adapter function usage" section (currently recommends CustomIntrinsicAdapter directly).
  • docs/docs/advanced/lora-and-alora-adapters.md — "Use the adapter in Mellea" section already self-flags as pending this issue; update it.
  • docs/docs/integrations/openai.mdpriority: the "load adapters manually" section has no deprecation caveat today (unlike the other two docs) and reads as the unqualified recommended way. Most likely to mislead someone who reads it before this issue lands.
  • docs/examples/aLora/101_example.py, docs/examples/aLora/README.md, docs/examples/aLora/stembolts_intrinsic.py, docs/examples/granite-switch/manual_adapter_loading.py, docs/examples/granite-switch/README.md, docs/examples/intrinsics/intrinsics.py — all construct a shim class directly as their canonical pattern; update to the composed path.

Tutorials (write here, not deferred to PR2):

  1. "Adding a custom adapter function in 20 lines" — replaces the CustomIntrinsicAdapter monkey-patch story.
  2. "Handling a breaking schema change without breaking users" — requirement-check v1→v2 worked example; HF revision pinning and AdapterSchemaMismatchError.
  3. "Reading adapter function telemetry" — dashboard-building guide referencing mellea.adapter_function.parse_failures, mellea.adapter_function.phase_duration, and the span tree.

PR1 acceptance criteria:

  • Both backends accept a composed Adapter directly; existing shim path unchanged/still passing
  • New unit + integration + real-GPU e2e coverage for the composed path (see Tests above)
  • The five shim-routed test files updated to also cover the composed path
  • CLI template generates composed-Adapter scaffolding
  • All 8 docs/examples files updated; docs/docs/integrations/openai.md explicitly prioritized
  • All three tutorials written, validated against current source, linked from docs/docs/advanced/intrinsics.md
  • markdownlint clean on all touched docs
  • Full test suite + mypy + ruff clean (existing CI gate — no new exemptions)

PR2 — delete the shims (stacked on PR1)

Goal: remove the now-genuinely-unused deprecated path. This PR's own green CI run against PR1-merged-into-main is the actual proof "we have no use left" — that's the validation, not a comment claiming it.

Code:

  • Delete IntrinsicAdapter, EmbeddedIntrinsicAdapter, CustomIntrinsicAdapter (mellea/backends/adapters/adapter.py) and their re-exports in mellea/backends/adapters/__init__.py.
  • Remove the now-dead isinstance branches in both backends that PR1 made redundant.

Tests:

  • Delete outright (these test the shim's own deprecated behaviour, not anything that survives): test/backends/test_adapters/test_shims.py, test/backends/test_adapters/test_adapter.py, and the from_hub/from_model_directory/construction tests in test/backends/test_adapters/test_embedded_adapter.py (keep any parts of that file that ended up testing the composed path instead, if PR1 added them there).

Final sweep (required, full-repo, not spot-check):

  • Re-run the same method used to scope this issue — gh api search/code for IntrinsicAdapter, EmbeddedIntrinsicAdapter, CustomIntrinsicAdapter across generative-computing/mellea — confirm zero hits outside historical/changelog text and docs/versioned_docs/ (old version snapshots, out of scope).
  • Confirm mellea/stdlib/components/intrinsic/intrinsic.py's module docstring (currently references IntrinsicAdapter descriptively) is updated.
  • Changelog entry: removal + migration note (import path change).

PR2 acceptance criteria:

  • Shim classes and re-exports removed
  • Full-repo sweep shows zero remaining references (code, docs, examples, CLI templates) outside changelog/historical text
  • Shim-behaviour test files deleted; nothing left asserting deprecated-class behaviour
  • Changelog entry with migration note
  • Full test suite + mypy + ruff clean
  • Closes this issue; triggers epic Epic: Fix Adapter Function Lifecycle & Consistency in Mellea #929 status reconciliation

Out of scope (either PR)

  • IBM adapter-function symbol rename — separate issue if not confirmed by the time this lands.
  • Any new functionality beyond the composed-path parity described above.
  • Epic Epic: Fix Adapter Function Lifecycle & Consistency in Mellea #929 items 3a (adapter name vs. intrinsic_name, still an open question per the epic's original text) and 6 (catalog removal/repurpose) — neither is resolved by this issue; flag separately if the epic is to close around this issue's completion.

Not part of this issue (confirmed clean elsewhere)

  • generative-computing/mellea-contribs — checked 2026-09-03, zero references to any of the three shim classes or old catalog-construction patterns. Nothing there needs updating.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area/adapter-functionsGranite adapter functions: framework and adaptiers including RAG, Guardian, CoredocumentationImprovements or additions to documentationp1High: important bugs (workaround exists) or high-value core features. Do soon, not on fire.refactor

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions