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
* Add caller-supplied invocation metadata (proposal 0034)
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).
* Carry caller metadata on synthetic checkpoint_migrated event
The synthetic NodeEvent dispatched for a checkpoint_migrated phase
in CompiledGraph.invoke was missed while wiring caller-invocation-
metadata snapshots through the regular _dispatch_started /
_dispatch_completed sites. Result: the
openarmature.checkpoint.migrate span ended up as the only span
missing the cross-cutting openarmature.user.* attribute set,
violating observability §5.6's "every span" invariant.
The metadata ContextVar is set 22 lines earlier in invoke(), so
current_invocation_metadata() returns the right value at this
synthetic dispatch site. Parallel to the three other dispatch
sites updated in PR #86.
0 commit comments