Skip to content

refactor(backends): route intrinsic generation through adapter_scope (Epic #929 Phase 2) #1465

Description

@planetf1

Parent epic: #929
Phase: 2
Depends on: #1141
Blocks: #1466 (generate / parse spans only)

Problem

Adapter-function generation does not run inside adapter_scope. LocalFileBinding
can now prepare / activate / deactivate / release a PEFT adapter, and
adapter_scope() demonstrably flips the real model's active adapter set — but the
model call in _generate_from_intrinsic still happens outside that scope, and the
standard generation path deactivates adapters first
(_generate_with_adapter_lock). So the lifecycle verbs and the generation they
exist to serve are not yet connected.

This was #1141's acceptance criterion 66 — "E2E adapter call (prepare →
activate → generate → deactivate → release) passes" — and it is not excluded by
#1141's out-of-scope list. It was deferred during implementation of PR #1454, and
the deferral is disclosed only in a test docstring
(test/backends/test_adapters/test_local_file_e2e.py) which refers to a follow-up
issue that was never created. This issue is that follow-up.

It is also not what the epic informally calls "4.1". 4.1 is #1144 (shim
removal plus the intrinsics_and_adapters.md rewrite), whose scope does not
mention generation, activation, scoping or locking. Without a separate issue this
work falls through the gap between the two.

Known constraint: the activation lock is not reentrant

_adapter_activation_lock returns _generation_lock, which is not reentrant.
Once generation runs inside adapter_scope and activate() re-acquires that
lock, it deadlocks. This was raised in PR #1454 review. Separately, the lock is
weaker than its docstring implies today — it does not serialise everything the
docstring claims — so the fix should reconcile behaviour and documentation rather
than only unblocking the deadlock.

Any solution needs to keep the current guarantee that a caller cannot observe
another caller's adapter active, which is what the existing
deactivate-before-generate behaviour buys.

Scope

  • Route the model call in _generate_from_intrinsic through adapter_scope, so
    the prepared and activated adapter is the one used for generation.
  • Resolve the activation/generation lock reentrancy, and align the docstring with
    actual behaviour.
  • Extend the existing e2e coverage so the full lifecycle is exercised with
    generation inside the scope, and remove the deferral note from
    test_local_file_e2e.py.

Out of scope

Acceptance criteria

  • _generate_from_intrinsic performs its model call inside adapter_scope
  • Generation demonstrably uses the activated adapter, asserted rather than
    smoke-tested
  • No deadlock: activate() inside adapter_scope with generation nested
    completes, with a regression test that would deadlock under the current lock
  • _adapter_activation_lock / _generation_lock docstrings match observed
    behaviour
  • A concurrent-callers test shows one caller cannot generate against another
    caller's adapter
  • test_local_file_e2e.py covers prepare → activate → generate → deactivate →
    release, and its deferral note is gone
  • uv run pytest test/ -m "not qualitative" passes; the e2e test passes on
    GPU hardware

References

Metadata

Metadata

Assignees

Labels

area/adapter-functionsGranite adapter functions: framework and adaptiers including RAG, Guardian, Corearea/aloraLoRA/aLora adapter trainingarea/backendsProvider-specific work: Ollama, HF, LiteLLM, OpenAI, Bedrock, vLLMp1High: 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