Skip to content

Wire real adapter_function_invocation_complete outcome for Embedded activation (follow-up to #1142) #1560

Closed

Description

@planetf1

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


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

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.

Metadata

Metadata

Assignees

Labels

area/adapter-functionsGranite adapter functions: framework and adaptiers including RAG, Guardian, Corearea/backendsProvider-specific work: Ollama, HF, LiteLLM, OpenAI, Bedrock, vLLMarea/telemetryOTel spans, metrics, tracing, semconv

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions