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
* Implement LlmFailedEvent typed variant (proposal 0058)
Carve LLM provider failures into a spec-normatively-typed event
variant alongside LlmCompletionEvent. Field set mirrors the success
variant's identity / scoping / request-side surface 1:1 (17 fields)
plus three failure-specific fields: error_category (always-present,
from the §7 normative category enumeration), error_type (optional
upstream class name or vendor code), error_message (always-present
human-readable from the raised exception).
OpenAIProvider.complete() restructures around the failure-event
emission: pre-send validation (validate_message_list / validate_tools
/ _normalize_response_schema) moves inside the try-block so any §7
category exception — pre-send OR adapter-caught — flows through the
same LlmFailedEvent path. The exception still raises out of
complete() unchanged; the typed event is dispatched on the observer
queue alongside the exception per proposal 0058's §6 dispatch
contract.
Both bundled observers (OTel + Langfuse) consume LlmFailedEvent
directly with the same openarmature.llm.complete span / Generation
shape as the success path plus ERROR status / level and the
openarmature.error.category attribute. Sentinel-namespace NodeEvent
emission for LLM events retires entirely from the bundled provider;
_make_llm_event is removed. LlmEventPayload + LLM_NAMESPACE remain
in observability/llm_event.py as a documented compatibility surface
for custom providers.
Spec pin advances from v0.51.0 to v0.53.0; proposal 0023 (canonical
state reducers) marked not-yet with fixtures 034-038 parser-
deferred. Fixtures 069-073 (the 0058 conformance set) deferred
pending typed_event_collector schema + the event_counts list
directive in the harness; unit tests pin the contract end-to-end:
9-category field-mapping lockdown, pre-send validation raise,
mutual-exclusion between LlmCompletionEvent and LlmFailedEvent on
the same call.
* Address PR 144 review
Two stale-content fixes flagged by CoPilot:
1. CHANGELOG line-17 and line-18 bullets carried "Failure paths
continue to fire from the sentinel NodeEvent" framing from the
3b/3c era, which contradicts this PR's LlmFailedEvent migration
and full sentinel retirement. Trimmed both fragments and added
a forward-reference to the proposal 0058 entry that documents
the cycle-final state.
2. AGENTS.md's reducer baseline reproduced proposal 0023's factory
reducers verbatim from the spec, but Python doesn't ship them
in this cycle (manifest 0023 = not-yet). The text is auto-
generated by build_agents_md.py from the pinned spec submodule;
updated the generator's lead paragraph to flag that capability
summaries reproduce spec content verbatim — including additions
from accepted proposals this implementation may not yet ship —
and point readers at conformance.toml for per-proposal impl
status. Generalizes to any future not-yet proposal landing in
spec text before Python catches up. Regenerated AGENTS.md.
Copy file name to clipboardExpand all lines: CHANGELOG.md
+8-2Lines changed: 8 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -6,10 +6,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The
6
6
7
7
## [Unreleased]
8
8
9
+
### Added
10
+
11
+
- **`LlmFailedEvent` typed event variant** (proposal 0058, spec v0.53.0). Carves LLM provider failures into a spec-normatively-typed event variant alongside `LlmCompletionEvent`. 17 mirrored identity / scoping / request-side fields + 3 failure-specific fields (`error_category` always-present from the llm-provider §7 normative category enumeration; optional `error_type` for vendor-specific detail or upstream exception class name; always-present `error_message`). `OpenAIProvider.complete()` emits the typed event alongside the §7 exception on both raise paths — adapter-caught provider exceptions AND pre-send validation raises. Caller-side exception flow unchanged; the exception still raises out of `complete()`. Mutually exclusive with `LlmCompletionEvent` on the same call. Both bundled observers (OTel + Langfuse) consume `LlmFailedEvent` directly: same `openarmature.llm.complete` span / Generation shape as the success path with ERROR status / level + `openarmature.error.category` attribute (OTel) / `error_category` as statusMessage (Langfuse), `start_time` back-dated by `latency_ms` so the failure duration reflects the time-to-raise.
12
+
9
13
### Changed
10
14
11
-
-**OTel and Langfuse observers drive the `openarmature.llm.complete` span / Generation observation lifecycle from the typed `LlmCompletionEvent`** (proposal 0049 + 0057, observability §5.5.7). Successful LLM-provider calls now open + close the OTel span and the Langfuse Generation in one shot at typed-event arrival, with `start_time` back-dated by `LlmCompletionEvent.latency_ms` so duration reflects the adapter-boundary measurement rather than dispatcher queue delay. Failure paths continue to fire from the sentinel `NodeEvent` (the typed event is success-only per the proposal). The §5.5 attribute set and §8.4 Generation metadata are unchanged.
12
-
-**`OpenAIProvider.complete()` no longer emits the sentinel `NodeEvent` pair on the success path** (v0.13.0 cleanup). The bundled OTel and Langfuse observers now consume the typed `LlmCompletionEvent` directly; the sentinel pair was kept on the success path through earlier releases for compatibility with pre-typed-event observers. External custom observers that filtered LLM calls by `event.namespace == LLM_NAMESPACE` MUST migrate to `isinstance(event, LlmCompletionEvent)` to continue seeing successful LLM calls. The sentinel `completed` event still fires on the failure path until the spec extends `LlmCompletionEvent` with error semantics; the sentinel `started` event is no longer emitted on either path.
15
+
-**Sentinel-namespace `NodeEvent` emission for LLM events retired entirely from `OpenAIProvider`** (proposal 0058 cleanup). The provider no longer dispatches the `("openarmature.llm.complete",)`-namespaced `NodeEvent`s on either outcome path; both success and failure flow through their respective typed variants exclusively. The `_make_llm_event` helper is removed. External custom observers that filtered LLM calls by `event.namespace == LLM_NAMESPACE` MUST migrate to `isinstance(event, LlmCompletionEvent)` for success and `isinstance(event, LlmFailedEvent)` for failure to keep receiving LLM-call notifications. `LlmEventPayload` and `LLM_NAMESPACE` remain in `openarmature.observability.llm_event` as a documented compatibility surface for custom providers that haven't migrated; neither is referenced by the bundled provider or observers anymore.
16
+
-**Pinned spec advances from v0.51.0 to v0.53.0** (absorbs proposals 0023 + 0058). Proposal 0023 (canonical state reducers) ships in spec v0.52.0 but is not implemented this cycle — `conformance.toml` marks 0023 as `not-yet`; fixtures 034–038 stay parser-deferred.
17
+
-**OTel and Langfuse observers drive the `openarmature.llm.complete` span / Generation observation lifecycle from the typed `LlmCompletionEvent`** (proposal 0049 + 0057, observability §5.5.7). Successful LLM-provider calls now open + close the OTel span and the Langfuse Generation in one shot at typed-event arrival, with `start_time` back-dated by `LlmCompletionEvent.latency_ms` so duration reflects the adapter-boundary measurement rather than dispatcher queue delay. The §5.5 attribute set and §8.4 Generation metadata are unchanged. (Failure paths land on `LlmFailedEvent` later in the same cycle — see the proposal 0058 entry above.)
18
+
-**`OpenAIProvider.complete()` no longer emits the sentinel `NodeEvent` pair on the success path** (v0.13.0 cleanup). The bundled OTel and Langfuse observers now consume the typed `LlmCompletionEvent` directly; the sentinel pair was kept on the success path through earlier releases for compatibility with pre-typed-event observers. External custom observers that filtered LLM calls by `event.namespace == LLM_NAMESPACE` MUST migrate to `isinstance(event, LlmCompletionEvent)` to continue seeing successful LLM calls. (The failure-path sentinel emission is retired entirely later in the same cycle — see the proposal 0058 entry above.)
13
19
-**`LangfuseClient` Protocol gains optional `start_time` / `end_time` timestamps** on `generation(...)` and the Generation/Span handles' `end(...)`. The Langfuse observer passes back-dated timestamps on the typed-event success path so the Langfuse UI shows the actual adapter-boundary duration. The SDK adapter handles v4 Langfuse SDK quirks transparently: `Langfuse.start_observation()` does NOT accept `start_time`, so back-dated generations are routed through the private `_otel_tracer.start_span(name=..., start_time=int_ns)` API (mirroring the SDK's own `create_event` precedent) and the resulting OTel span is wrapped in `LangfuseGeneration` directly; the non-back-dated path still uses `start_observation`. `LangfuseSpan.end()` is typed `Optional[int]` (nanoseconds), so the adapter converts the Protocol's `datetime` surface to int nanoseconds before forwarding. The `InMemoryLangfuseClient` stores both fields verbatim on `LangfuseObservation` for test assertions.
14
20
-**`OpenAIProvider(populate_caller_metadata=...)` default flipped from `False` to `True`.** The python implementation now populates `LlmCompletionEvent.caller_invocation_metadata` by default so the bundled OTel and Langfuse observers can emit the §5.6 `openarmature.user.<key>` span-attribute family without a separate opt-in. Pass `populate_caller_metadata=False` to suppress the snapshot when no downstream consumer needs it. The spec-defined opt-in mechanism is unchanged; only the python default flips.
Copy file name to clipboardExpand all lines: src/openarmature/AGENTS.md
+67-7Lines changed: 67 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,6 +1,6 @@
1
1
# OpenArmature — Agent documentation
2
2
3
-
*This is the agent guide bundled with the openarmature Python package, version 0.12.0 (spec v0.51.0). For the full docs site see [openarmature.ai](https://openarmature.ai). For the canonical spec text see [openarmature.org/capabilities](https://openarmature.org/capabilities/). For project-specific conventions for the code you're editing, see the host project's `AGENTS.md` or `CLAUDE.md`.*
3
+
*This is the agent guide bundled with the openarmature Python package, version 0.12.0 (spec v0.53.0). For the full docs site see [openarmature.ai](https://openarmature.ai). For the canonical spec text see [openarmature.org/capabilities](https://openarmature.org/capabilities/). For project-specific conventions for the code you're editing, see the host project's `AGENTS.md` or `CLAUDE.md`.*
4
4
5
5
## TL;DR
6
6
@@ -10,7 +10,7 @@ OpenArmature is a workflow framework for LLM pipelines and tool-calling agents:
10
10
11
11
## Capability contracts
12
12
13
-
_Sourced from openarmature-spec v0.51.0. Each entry below reproduces §1 (Purpose) and §2 (Concepts) of the capability's `spec.md`. For the full spec text (execution model, error semantics, determinism, observer hooks, etc.) see the linked docs site._
13
+
_Sourced from openarmature-spec v0.53.0. Each entry below reproduces §1 (Purpose) and §2 (Concepts) of the capability's `spec.md` verbatim — including additions from accepted proposals that this Python implementation may not yet ship. For per-proposal implementation status (implemented / partial / textual-only / not-yet), see the `conformance.toml` manifest at the repo root. For the full spec text (execution model, error semantics, determinism, observer hooks, etc.) see the linked docs site._
14
14
15
15
### Capability: `graph-engine`
16
16
@@ -46,11 +46,15 @@ engine constant, not a reserved node name, so a user node may happen to be named
46
46
47
47
**Reducer.** A function that merges a node's partial update into the prior state for a given field. Each state
48
48
field has exactly one reducer. The default reducer is _last-write-wins_ (the new value replaces the old).
49
-
Implementations MUST provide at least: `last_write_wins`, `append` (for list-typed fields), `merge`
50
-
(for mapping-typed fields), `concat_flatten` (for list-typed fields whose updates are lists of lists —
0 commit comments