You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Implements observability §3.4 + §5.6 + §8.4.1/§8.4.2: callers pass
``invoke(metadata={...})`` and the framework propagates the entries
to every observability backend.
Public surface:
- ``compiled.invoke(metadata=...)`` accepts a per-invocation
``dict[str, AttributeValue]`` mapping where values are OTel
scalars or homogeneous arrays (str/int/float/bool).
- ``openarmature.observability.set_invocation_metadata(**entries)``
augments the in-scope mapping mid-invocation (additive merge;
existing keys overwritten).
- ``openarmature.observability.current_invocation_metadata()``
reads the in-scope mapping (returns empty MappingProxyType
outside an invocation).
- Synchronous validation at the API boundary rejects keys under
the reserved ``openarmature.*`` / ``gen_ai.*`` prefixes and
rejects non-OTel-compatible value types (``ValueError`` before
any work begins).
Engine internals:
- ContextVar lifecycle in
``openarmature.observability.metadata``; engine drives
``_set_invocation_metadata`` / ``_reset_invocation_metadata``
around the outermost ``invoke()`` call.
- ``NodeEvent.caller_invocation_metadata`` carries a dispatch-time
snapshot (the deliver_loop task's Context is frozen at invoke
time, so observers can't re-read the live ContextVar safely).
- ``LlmEventPayload.caller_invocation_metadata`` mirrors the
pattern for LLM provider events.
OTel observer (§5.6): emits each entry as
``openarmature.user.<key>`` on every span — invocation, node,
subgraph wrapper, fan-out instance dispatch, LLM provider,
detached roots, checkpoint-migrate, checkpoint-save. Cross-cutting
attribute family parallel to ``openarmature.correlation_id``.
Langfuse observer (§8.4.1 + §8.4.2): merges each entry as a
top-level key into ``trace.metadata`` (at trace open) and into
every observation's ``metadata`` bag (leaf nodes, subgraph
wrappers, fan-out instance dispatches, detached-trace wrappers
+ link observations, LLM generations).
Conformance fixtures (proposal 0035's full set is 026 / 027 / 028
/ 029 / 030):
- 026 (OTel cross-cutting) and 027 (Langfuse top-level merge)
activated.
- 028 (boundary rejection) activated via a dedicated
``_run_fixture_028`` runner that attaches both observers and
asserts ``ValueError`` + no spans + no Langfuse traces.
- 029 (fan-out per-instance) and 030 (parallel-branches per-branch)
stay deferred — they need an ``augment_metadata_from_field``
harness primitive that calls ``set_invocation_metadata`` per
fan-out instance / per parallel branch. The augmentation surface
is already exercised by unit tests; the conformance fixtures
un-defer in a follow-up.
Adds 25 focused unit tests covering boundary validation, ContextVar
lifecycle, augmentation merge / overwrite, reserved-namespace
rejection, ``invoke()``-boundary rejection, mid-invocation
augmentation persisting to subsequent nodes,
``openarmature.user.*`` emission on every OTel span, and Langfuse
top-level merge on trace + every observation.
The OTel-side LLM-payload runner was extended to handle multi-node
graphs where ``calls_llm`` is on a non-entry node (fixture 026 has
a ``prep`` step before the LLM call).
0 commit comments