Repository navigation
Wire real adapter_function_invocation_complete outcome for Embedded activation (follow-up to #1142) #1560
Description
Epic: #929 | Blocked by: #1465 (merged 2026-08-25) | Phase: 2
Problem
EmbeddedBinding.apply_activation (mellea/backends/adapters/_core.py) only
fires adapter_function_phase_complete (phase "activate") — it
deliberately does not fire adapter_function_invocation_complete.
That's because apply_activation only edits the outgoing request;
OpenAIBackend resolves the real generation + parse outcome later, lazily,
once the caller awaits the resulting ModelOutputThunk (see
granite_formatters_processing in mellea/backends/openai.py). Firing
invocation_complete from apply_activation would mean guessing an
outcome the method can't know — it was originally hardcoded to "success",
which was flagged in review on #1142 / PR #1559 (a Granite Switch call that
failed or returned malformed JSON still recorded success) and fixed by
removing that fire rather than reporting wrong data.
Practical effect: mellea.adapter_function.invocations and
.parse_failures never increment for binding_type="embedded" today.
Proposed direction
Fire adapter_function_invocation_complete from OpenAIBackend once
generation and parsing actually resolve (success/schema_error/error),
mirroring how AdapterMixin.adapter_scope's finally block does this for
LocalFileBinding. This likely depends on #1465 (wiring real generation
through adapter_scope) landing first, or an equivalent completion hook for
the OpenAI intrinsic path — _generate_from_intrinsic doesn't currently
have a single synchronous point where the outcome is known.
References
- feat(backends): EmbeddedBinding implements apply_activation (Granite Switch path); remove render_controls + set_request_adapter (Epic #929 Phase 2) #1142 / PR feat(backends): EmbeddedBinding implements apply_activation (Granite Switch path); remove render_controls + set_request_adapter (Epic #929 Phase 2) #1559 — where this gap was found and the hardcoded-success
version was fixed by removing the fire - refactor(backends): route intrinsic generation through adapter_scope (Epic #929 Phase 2) #1465 — wiring real generation through
adapter_scope(likely
prerequisite) - feat(telemetry): add complete adapter-function lifecycle observability #1466 — adapter-function span emission (same underlying "no completion
signal yet" family of gaps)
Update (2026-09-02) — scope now covers LocalHFBackend too, plus explicit acceptance criteria
#1018 (PR #1593, merged 2026-09-02) added a second production caller of
EmbeddedBinding.apply_activation: LocalHFBackend._generate_embedded_with_generation_lock
(mellea/backends/huggingface.py:696, invoked from _generate_from_intrinsic
at huggingface.py:943). It has the identical gap described above — no
adapter_function_invocation_complete fires for the local embedded path
either, so binding_type="embedded" invocations/parse-failures under-count
for both backends now, not just OpenAIBackend.
Both backends resolve the real outcome via their own async
granite_formatters_processing closure (openai.py:905,
huggingface.py:1041) — same shape, separate call sites. Scope is
therefore: fix the completion signal in both closures, not OpenAIBackend
alone; this likely wants one shared helper both closures call, given the
duplicated shape.
Acceptance criteria
-
mellea.adapter_function.invocationsincrements exactly once per
embedded adapter-function call onOpenAIBackend, with the real
outcome(success/schema_error/error) - Same, for
LocalHFBackendembedded calls -
.parse_failuresincrements when parsing fails on either backend, not
only when generation itself errors - A malformed-JSON / schema-mismatch response records
schema_error,
notsuccess— the exact bug feat(backends): EmbeddedBinding implements apply_activation (Granite Switch path); remove render_controls + set_request_adapter (Epic #929 Phase 2) #1142/PR feat(backends): EmbeddedBinding implements apply_activation (Granite Switch path); remove render_controls + set_request_adapter (Epic #929 Phase 2) #1559 review caught and fixed
by removing the hardcoded fire; add a regression test pinning this - Existing
binding_type="local_file"invocation-complete behaviour
(viaadapter_scope) is unchanged
Related test infra
test/backends/test_adapters/test_embedded_adapter.py,
test_embedded_binding.py, and test_embedded_integration.py (added by
#1593) are the closest existing embedded-path test files/hook-capture
patterns (_hook_capture.py) to extend.
Dependencies
Not blocked — #1465 merged 2026-08-25. #1018/#1593 (merged 2026-09-02) is
additive context that widens scope, not a sequencing blocker.
Activity
fix: cover OpenAI's self.processing() call in the invocation_complete…
refactor: trim remaining verbose comments/docstrings, point follow-up…
fix(backends): fire adapter_function_invocation_complete for embedded…