Skip to content

Commit 54db87e

Browse files
Pin spec v0.54.0 and rename provider-payload flag
Advance the pinned spec submodule from v0.53.0 to v0.54.0 (proposal 0059, retrieval-provider / embedding capability) and adopt the proposal's one cross-spec consequence: rename the observer-level privacy flag disable_llm_payload to disable_provider_payload on both bundled observers. This is a breaking change to the OTelObserver and LangfuseObserver constructors; callers update one config key with no behavioral change. The flag's scope broadens from LLM-completion payload to any provider-call payload. The embedding capability itself is not implemented in this cycle; it rides as not-yet in conformance.toml. Only the observability 5.5.4 rename lands with the pin, since it touches existing LLM-payload gating. Sync the three spec-version declarations (__spec_version__, pyproject.toml, the smoke-test assertion), regenerate AGENTS.md, and defer the new embedding parsing fixtures (observability 074-083).
1 parent 272b0dd commit 54db87e

22 files changed

Lines changed: 105 additions & 62 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The
1515

1616
- **`RetryMiddleware` now takes a `RetryConfig` record** instead of individual constructor kwargs (proposal 0050 prep). The four retry settings (`max_attempts` / `classifier` / `backoff` / `on_retry`, each optional) move onto a frozen `RetryConfig`; construct as `RetryMiddleware(RetryConfig(max_attempts=...))`, while bare `RetryMiddleware()` still applies the defaults. This is a breaking change to the `RetryMiddleware` constructor. The record is the same shape the upcoming call-level `complete(retry=...)` parameter will accept, so one retry config serves both the per-node and per-call layers. `None` fields resolve to the canonical defaults (`default_classifier` / `exponential_jitter_backoff`) at use, preserving the prior behavior.
1717
- **Failure-isolation events report the originating cause's category at non-node placements** (proposal 0065, pipeline-utilities §6.3). When `FailureIsolationMiddleware` runs as instance middleware (§9.7), branch middleware (§11.7), or parent-node middleware on a fan-out / parallel-branches node, the graph engine has already wrapped the originating error as a `node_exception` carrier before the middleware catches it. `FailureIsolatedEvent.caught_exception.category` now resolves through that carrier (and any nested carriers) to the nearest categorized originating cause and reports its category instead of the masking `node_exception`, so the reported category agrees with what the §6.1 retry classifier acted on. For example, an instance whose retries exhaust on `provider_unavailable` now surfaces `provider_unavailable` rather than `node_exception`. The `message` tracks the resolved cause for category/message coherence. Node-level placement was already faithful and is unchanged, and catch/degrade behavior is unchanged at every site (only the event's reported cause changes). The wrapped-instance/branch lineage SHOULD (`fan_out_index` / `branch_name`) is deferred to a follow-up, since it needs the engine to surface per-instance identity to the wrapping-site middleware.
18+
- **Observer privacy flag `disable_llm_payload` renamed to `disable_provider_payload`** (proposal 0059, observability §5.5.4, spec v0.54.0). The observer-level flag on both bundled observers (`OTelObserver` and `LangfuseObserver`) is renamed, and its scope broadens from LLM-completion payload to any provider-call payload (LLM completion today; embedding and rerank when those land). This is a breaking change to both observer constructors: config passing `disable_llm_payload=True` (or `False`) updates to `disable_provider_payload=...` with no other change. The default stays `True` (payload suppressed), and the gating behavior for `LlmCompletionEvent` / `LlmFailedEvent` rendering is unchanged at every existing site. The rename is the only part of proposal 0059 adopted this cycle: the retrieval-provider capability itself (the `EmbeddingProvider` protocol, the `EmbeddingEvent` / `EmbeddingFailedEvent` typed variants, and the embedding span / observation mapping) is not yet implemented and rides as `not-yet` in `conformance.toml`. The §5.5.4 rename touches existing LLM-payload gating, so it lands with the pin. Pinned spec advances v0.53.0 → v0.54.0.
1819

1920
## [0.13.0] — 2026-06-09
2021

conformance.toml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232

3333
[manifest]
3434
implementation = "openarmature-python"
35-
spec_pin = "v0.53.0"
35+
spec_pin = "v0.54.0"
3636

3737
# Status values:
3838
# implemented — shipped behavior matches the proposal's contract
@@ -454,9 +454,9 @@ since = "0.12.0"
454454
# not:
455455
# ``tests/unit/test_observability_otel.py::
456456
# test_invocation_span_carries_implementation_attribution_attributes``;
457-
# - OTel always-emit invariant under ``disable_llm_payload``,
457+
# - OTel always-emit invariant under ``disable_provider_payload``,
458458
# ``disable_genai_semconv``, ``disable_llm_spans``:
459-
# ``::test_invocation_span_attribution_emits_under_disable_llm_payload``;
459+
# ``::test_invocation_span_attribution_emits_under_disable_provider_payload``;
460460
# - OTel attributes emit on every invocation span across a
461461
# reused observer (3 sequential invocations):
462462
# ``::test_invocation_span_attribution_emits_on_every_invocation``;
@@ -593,3 +593,15 @@ since = "0.13.0"
593593
[proposals."0058"]
594594
status = "implemented"
595595
since = "0.13.0"
596+
597+
# Spec v0.54.0 (proposal 0059). Retrieval-provider capability —
598+
# the ``EmbeddingProvider`` protocol + ``EmbeddingEvent`` /
599+
# ``EmbeddingFailedEvent`` typed variants + OTel/Langfuse embedding
600+
# mapping. Python has not yet shipped the embedding surface, so the
601+
# capability is not-yet. The one piece adopted at this pin is the
602+
# proposal's cross-spec consequence: the observer-level privacy flag
603+
# ``disable_llm_payload`` is renamed ``disable_provider_payload`` (the
604+
# §5.5.4 rename touches existing LLM-payload gating, so it lands with
605+
# the pin even though the embedding capability does not).
606+
[proposals."0059"]
607+
status = "not-yet"

docs/agent/non-obvious-shapes.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ else:
4848

4949
The discriminator is one branch; missing it gives you empty data on tool-call responses and silently wrong behavior on truncations.
5050

51-
### `disable_llm_payload` defaults to `True`: flip it for LLM-aware observability backends
51+
### `disable_provider_payload` defaults to `True`: flip it for LLM-aware observability backends
5252

53-
The `OTelObserver` (and any spec-conformant observer reading LLM events) defaults `disable_llm_payload: bool = True` per spec §5.5's "default-off by privacy" framing. Without flipping the flag, LLM spans carry GenAI semconv attributes (token counts, model name, finish reason) but NOT the message payload (input messages, response content, request extras).
53+
The `OTelObserver` (and any spec-conformant observer reading LLM events) defaults `disable_provider_payload: bool = True` per spec §5.5's "default-off by privacy" framing. Without flipping the flag, LLM spans carry GenAI semconv attributes (token counts, model name, finish reason) but NOT the message payload (input messages, response content, request extras).
5454

5555
That's the right default for general OpenArmature use: payloads may contain PII the user hasn't audited, and storage cost grows with prompt size. But it's the WRONG default if you're wiring up an LLM-aware observability backend (Langfuse, Phoenix, Honeycomb's LLM lens) that renders the message stream as part of its generation view. Backends will show "empty" generations and you'll wonder why.
5656

@@ -61,7 +61,7 @@ from openarmature.observability import OTelObserver
6161

6262
observer = OTelObserver(
6363
span_processor=your_exporter,
64-
disable_llm_payload=False, # opt in to message-payload attributes
64+
disable_provider_payload=False, # opt in to message-payload attributes
6565
)
6666
graph.attach_observer(observer)
6767
```

docs/concepts/observability.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -702,12 +702,12 @@ source on your stack.
702702
### LLM payload attributes
703703

704704
By default, LLM spans do **not** carry the messages sent or the
705-
response content. Opt in with `disable_llm_payload=False`:
705+
response content. Opt in with `disable_provider_payload=False`:
706706

707707
```python
708708
observer = OTelObserver(
709709
span_processor=SimpleSpanProcessor(exporter),
710-
disable_llm_payload=False,
710+
disable_provider_payload=False,
711711
)
712712
```
713713

@@ -764,7 +764,7 @@ level (per llm-provider §3.1.2); only `source` is replaced. URL-form
764764
images pass through unchanged: the URL is a short string and is
765765
informative for trace readers.
766766

767-
Redaction is **not** gated by `disable_llm_payload` and is **not**
767+
Redaction is **not** gated by `disable_provider_payload` and is **not**
768768
configurable. Inline image bytes never leave the provider in event
769769
form, so custom observers consuming
770770
[`LlmCompletionEvent` / `LlmFailedEvent`](#consuming-llm-events-in-custom-observers)
@@ -967,7 +967,7 @@ langfuse_client = Langfuse(
967967
)
968968
observer = LangfuseObserver(
969969
client=LangfuseSDKAdapter(langfuse_client),
970-
disable_llm_payload=False,
970+
disable_provider_payload=False,
971971
)
972972
```
973973

@@ -1014,15 +1014,15 @@ for a runnable demo.
10141014

10151015
### Payload + truncation
10161016

1017-
`disable_llm_payload` mirrors the OTel observer's flag and defaults
1017+
`disable_provider_payload` mirrors the OTel observer's flag and defaults
10181018
to `True` for the same privacy reason. Flip to `False` to populate
10191019
`generation.input` / `output` / `metadata.request_extras` from the
10201020
LLM event payload.
10211021

10221022
```python
10231023
observer = LangfuseObserver(
10241024
client=client,
1025-
disable_llm_payload=False,
1025+
disable_provider_payload=False,
10261026
payload_byte_cap=65536,
10271027
)
10281028
```
@@ -1057,5 +1057,5 @@ graph.attach_observer(otel_observer)
10571057
graph.attach_observer(langfuse_observer)
10581058
```
10591059

1060-
Each observer's `disable_llm_spans` / `disable_llm_payload` flag is
1060+
Each observer's `disable_llm_spans` / `disable_provider_payload` flag is
10611061
independent; one MAY emit while the other suppresses.

docs/examples/langfuse-observability.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ manual wiring at the call site.
3939
surfaces it on every Generation that renders from that prompt.
4040
Filesystem / in-memory backends without that reference work too,
4141
they just produce metadata-only linkage.
42-
- `disable_llm_payload=False` opt-in for capturing input messages +
42+
- `disable_provider_payload=False` opt-in for capturing input messages +
4343
output content on Generation observations. Default-off is the
4444
privacy posture; the demo deliberately flips it.
4545
- `correlation_id` cross-cutting metadata on the Trace and every
@@ -140,7 +140,7 @@ langfuse_client = Langfuse(
140140
)
141141
observer = LangfuseObserver(
142142
client=LangfuseSDKAdapter(langfuse_client),
143-
disable_llm_payload=False,
143+
disable_provider_payload=False,
144144
)
145145
```
146146

@@ -174,7 +174,7 @@ graph.attach_observer(OTelObserver(span_processor=batch))
174174
graph.attach_observer(LangfuseObserver(client=langfuse_client))
175175
```
176176

177-
Their `disable_llm_spans` / `disable_llm_payload` flags are
177+
Their `disable_llm_spans` / `disable_provider_payload` flags are
178178
independent. The `correlation_id` cross-cutting attribute is the join
179179
key: find a slow Generation in Langfuse, search for the
180180
`correlation_id` in OTel logs to see the surrounding infrastructure

docs/examples/production-observability.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ answer: The primary objective of Apollo 11 was ...
175175
model: gpt-4o-mini-2024-07-18
176176
177177
--- captured OTel spans ---
178-
[openarmature.invocation] 1240.0ms openarmature.graph.entry_node='respond', openarmature.graph.spec_version='0.53.0', openarmature.implementation.name='openarmature-python', openarmature.implementation.version='0.13.0'
178+
[openarmature.invocation] 1240.0ms openarmature.graph.entry_node='respond', openarmature.graph.spec_version='0.54.0', openarmature.implementation.name='openarmature-python', openarmature.implementation.version='0.13.0'
179179
[respond] 1235.0ms openarmature.node.name='respond', openarmature.user.tenantId='demo-acme', ...
180180
[openarmature.llm.complete] 1200.0ms openarmature.user.tenantId='demo-acme', gen_ai.system='openai', gen_ai.usage.input_tokens=42, ...
181181
[persist] 2.0ms openarmature.node.name='persist', openarmature.user.tenantId='demo-acme', ...
@@ -289,7 +289,7 @@ langfuse_observer = LangfuseObserver(
289289
),
290290
trace_input_from_state=_trace_input,
291291
trace_output_from_state=_trace_output,
292-
disable_llm_payload=False,
292+
disable_provider_payload=False,
293293
)
294294
```
295295

examples/langfuse-observability/main.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,13 +262,13 @@ async def main() -> None:
262262
# ``LangfuseClient`` Protocol; the observer code doesn't change.
263263
client = InMemoryLangfuseClient()
264264

265-
# disable_llm_payload=False opts in to capturing the input messages
265+
# disable_provider_payload=False opts in to capturing the input messages
266266
# and output content on Generation observations. Default is True
267267
# for the same privacy reason the OTel observer's flag exists:
268268
# payloads may contain PII the operator hasn't audited. Flip it
269269
# deliberately here because the demo's whole point is showing what
270270
# the model saw and returned.
271-
observer = LangfuseObserver(client=client, disable_llm_payload=False)
271+
observer = LangfuseObserver(client=client, disable_provider_payload=False)
272272

273273
graph = build_graph()
274274
graph.attach_observer(observer)

examples/production-observability/main.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -582,14 +582,14 @@ def build_graph() -> CompiledGraph[BriefingState]:
582582
# any OTLP-compatible backend.
583583
#
584584
# Caller hooks attach to LangfuseObserver via constructor kwargs.
585-
# ``disable_llm_payload=False`` opts in to capturing the input
585+
# ``disable_provider_payload=False`` opts in to capturing the input
586586
# messages + output content on Generation observations so the demo
587587
# output is meaningful; the default-True is the privacy-preserving
588588
# setting.
589589

590590

591591
def _build_otel_observer(exporter: InMemorySpanExporter) -> OTelObserver:
592-
# ``disable_llm_payload=False`` opts in to capturing input messages
592+
# ``disable_provider_payload=False`` opts in to capturing input messages
593593
# + output content on the LLM-call span (same flag the Langfuse
594594
# observer below flips for the same reason). The example's whole
595595
# point is showing both backends seeing the same logical events;
@@ -600,14 +600,14 @@ def _build_otel_observer(exporter: InMemorySpanExporter) -> OTelObserver:
600600
return OTelObserver(
601601
span_processor=SimpleSpanProcessor(exporter),
602602
resource=Resource.create({"service.name": "openarmature-production-observability"}),
603-
disable_llm_payload=False,
603+
disable_provider_payload=False,
604604
)
605605

606606

607607
def _build_langfuse_observer(client: InMemoryLangfuseClient) -> LangfuseObserver:
608608
return LangfuseObserver(
609609
client=client,
610-
disable_llm_payload=False,
610+
disable_provider_payload=False,
611611
trace_input_from_state=_trace_input,
612612
trace_output_from_state=_trace_output,
613613
)

openarmature-spec

Submodule openarmature-spec updated 56 files

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ Specification = "https://github.com/LunarCommand/openarmature-spec"
6363
openarmature = "openarmature.cli:main"
6464

6565
[tool.openarmature]
66-
spec_version = "0.53.0"
66+
spec_version = "0.54.0"
6767

6868
[dependency-groups]
6969
dev = [

0 commit comments

Comments
 (0)