Skip to content

refactor(intrinsics): internal migration with shims — old classes inherit from Adapter; call_intrinsic rewritten (Epic #929 Phase 1) #1136

@planetf1

Description

@planetf1

Parent epic: #929
Design proposal: PR #1080
Phase: 1 (Wave 2 — start after 0.1 has a draft PR)
Depends on: 0.1
Blocks: 1.B, 1.C, 1.D, 2.1, 4.1

⚠️ Phase 1 bottleneck. This is the only serialisation point before Wave 3 opens four parallel tracks. Keep the PR narrow (~300–500 LOC) to merge quickly.


Problem

mellea/stdlib/components/intrinsic/_util.py:call_intrinsic and mellea/stdlib/requirements/requirement.py (requirement rerouting) branch on the old IntrinsicAdapter / EmbeddedIntrinsicAdapter subclasses. Until these internal callers operate on the new Adapter type from 0.1, no helper file can migrate. External users may also construct the old classes directly — migrating internals without a backward-compat path breaks them.

Agreed design

Two tightly-coupled changes in one PR (splitting creates ordering pain and conflicting branch state):

(a) Old classes become inheriting shims

IntrinsicAdapter, EmbeddedIntrinsicAdapter, CustomIntrinsicAdapter are restructured to:

  • Inherit from Adapter (new dataclass from 0.1) — isinstance(x, IntrinsicAdapter) and isinstance(x, Adapter) both work
  • Translate constructor args into Identity + IOContract + WeightsBinding triple, then call Adapter.__init__
  • Emit a DeprecationWarning once per construction site (stacklevel=2) pointing at the new construction pattern
  • Carry no behavioural state of their own — delegate to inherited Adapter machinery

(b) Internal callers operate on Adapter

_util.call_intrinsic and requirement rerouting rewritten to:

adapter = backend.resolve_adapter(name)
with backend.adapter_scope(adapter):
    raw = backend.generate(adapter.io_contract.build_prompt(...))
return adapter.io_contract.parse(raw)
  • adapter_scope wraps activate()/deactivate() per call; this is the future telemetry parent — Phase 2 wraps it in an intrinsic.call OTel span; the hook point must exist at the right call-site boundary now
  • prepare() at session start (2.2); release() at session teardown
  • Role-based lookup for requirement rerouting uses Identity.role instead of isinstance branching
  • Backends grow stub resolve_adapter / adapter_scope implementations delegating to old code paths (real implementations in Phase 2)

Docs update

  • docs/dev/requirement_aLoRA_rerouting.md — update to describe role-based lookup (using Identity.role) instead of hardcoded requirement-check string. This resolves thread 7 in the design proposal's thread mapping.

Scope

  • mellea/backends/adapters/__init__.py — old classes as inheriting shims
  • mellea/stdlib/components/intrinsic/_util.pycall_intrinsic rewritten
  • mellea/stdlib/requirements/requirement.py — rerouting rewritten
  • Abstract backend + concrete backend stubs: resolve_adapter, adapter_scope (no instrumentation yet)
  • docs/dev/requirement_aLoRA_rerouting.md — role-based lookup documentation

Out of scope

Helper file migration (1.B–D), backend verb implementations (Phase 2), AdapterMixin rename (2.1), shim removal (4.1).

Acceptance criteria

  • IntrinsicAdapter(...) returns instance satisfying both isinstance(x, IntrinsicAdapter) and isinstance(x, Adapter)
  • Same for EmbeddedIntrinsicAdapter and CustomIntrinsicAdapter
  • Each old constructor emits exactly one DeprecationWarning per call (not per import), stacklevel=2
  • _util.call_intrinsic operates on Adapter; no isinstance branching on old subclasses
  • Requirement rerouting uses Identity.role
  • adapter_scope exists at the correct call-site boundary; pass-through context manager at this stage
  • docs/dev/requirement_aLoRA_rerouting.md updated; markdownlint passes
  • All existing tests pass (behavioural neutrality is the bar)
  • ruff format, ruff check, mypy clean

Test plan

test/backends/adapters/test_old_class_shims.py:

  • test_old_classes_inherit_from_adapter
  • test_old_constructor_emits_deprecation_warning — once per call, stacklevel=2
  • test_old_constructor_drop_in_replaceable — construct via old API, assert behaviour matches Adapter(...) directly

New tests for role-based lookup with multiple registered roles.

Breaking changes

  • Subclassing the old classes: anyone overriding internal methods of IntrinsicAdapter etc. may break. Public constructor signature preserved.
  • isinstance checks against the old Adapter ABC: depends on the naming-collision resolution chosen in 0.1.

Document both in the PR description with migration examples.

References

Metadata

Metadata

Assignees

Labels

area/backendsProvider-specific work: Ollama, HF, LiteLLM, OpenAI, Bedrock, vLLMarea/intrinsicsGranite intrinsic adapters: RAG, Guardian, Corearea/stdlibCore abstractions: Context, MOT, SamplingStrategy, formatters, serializationrefactor

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions