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

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, 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