Skip to content

Commit 5c0fa1c

Browse files
chore(release): v0.11.0-rc1 (#106)
* chore(release): v0.11.0-rc1 Observability + prompt-management release. Pinned spec advances from v0.27.1 to v0.38.0, absorbing eight accepted proposals (0039-0046). CHANGELOG flipped from [Unreleased] to [0.11.0] - 2026-06-01 with all eight proposals enumerated. Filled in missing entries for 0039 (caller-supplied invocation_id), 0040 (mid-invocation open-span metadata update), 0041 (Langfuse metadata key collision reservation), LangfusePromptBackend, and the install_log_bridge SDK-handler defensive check. Docs sweep: fixed stale openarmature.branch_name reference in the observability concept page (renamed to openarmature.node.branch_name by 0044); added a "Two variants: text and chat" subsection to the prompts concept page introducing the TextPrompt | ChatPrompt discriminated union. Version pins bumped to 0.11.0rc1 in pyproject.toml, src/openarmature/__init__.py, and tests/test_smoke.py. AGENTS.md regenerated. * Reword TextPrompt description as complete sentences CoPilot flagged the bullet as a fragment ("The simpler variant and the default for the filesystem backend..." — no main verb). Reworded as two complete clauses; no semantic change.
1 parent ccb2d29 commit 5c0fa1c

8 files changed

Lines changed: 46 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ All notable changes to `openarmature-python` are documented in this file.
44

55
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The package follows [Semantic Versioning](https://semver.org/); pre-1.0 minor bumps may carry behavioral changes per [spec governance](https://github.com/LunarCommand/openarmature-spec/blob/main/GOVERNANCE.md).
66

7-
## [Unreleased]
7+
## [0.11.0] — 2026-06-01
8+
9+
Observability + prompt-management release. The pinned spec advances from v0.27.1 to v0.38.0, absorbing eight accepted proposals (0039-0046). Two headlines: (1) the Langfuse observer grows native `trace.input` / `trace.output` sourcing with caller hooks (0043) and the per-async-context augmentation boundary becomes lineage-aware for nested fan-out / parallel-branches topologies (0045); (2) prompt-management gains a Chat-prompt variant alongside the existing Text-prompt (0046) and `LangfusePromptBackend` lands for both Langfuse text and chat prompts. Caller-supplied `invocation_id` (0039), mid-invocation open-span metadata update (0040), three reserved-key surfaces (0041 + 0042), and the parallel-branches OTel dispatch span (0044) round out the cycle.
810

911
### Added
1012

@@ -25,6 +27,9 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The
2527
- `openarmature.parallel_branches.branch_count` + `openarmature.parallel_branches.error_policy` on the parallel-branches NODE span.
2628
- `openarmature.node.branch_name` + `openarmature.parallel_branches.parent_node_name` on each per-branch dispatch span.
2729
- `openarmature.node.branch_name` on every inner-node span beneath a per-branch dispatch span.
30+
- **Caller-supplied `invocation_id`** (proposal 0039, observability §5.1, spec v0.32.0). `invoke(invocation_id=...)` now accepts a caller-supplied non-empty URL-safe string in place of the framework-minted UUIDv4. Mirrors the `correlation_id` shape: caller-supplied wins; framework mints a UUIDv4 only when absent. Resume mints a fresh `invocation_id` per attempt (the previous attempt's id remains on the saved record). The Langfuse mapping derives `trace.id` via the SDK's `create_trace_id(seed=invocation_id)` for non-UUID values (raw id preserved under `trace.metadata.invocation_id`); UUID values continue to map via dashes-stripped hex.
31+
- **Mid-invocation open-span metadata update** (proposal 0040, observability §3.4, spec v0.31.0). `set_invocation_metadata(**entries)` called mid-invocation now updates currently-open spans in the augmenting async context in place, via the backend SDK's attribute / metadata update path (`Span.set_attribute` for OTel, observation `update(metadata=...)` for Langfuse). Tightens 0034's per-async-context delivery from SHOULD to MUST; preserves the ancestor / sibling boundary (spans in ancestor or sibling contexts MUST NOT be updated). Per spec §3.4 v0.31.0; proposal 0045 (v0.37.0) extends the boundary rule to be lineage-aware for nested dispatch.
32+
- **`LangfusePromptBackend`** (text and chat variants). A `PromptBackend` impl backed by the Langfuse SDK's prompt-registry surface. Gated behind the existing `[langfuse]` extra so the base package stays SDK-free. Maps Langfuse `TextPromptClient` to `TextPrompt`; `ChatPromptClient` to `ChatPrompt` (added by proposal 0046 — see entry above). Fails closed (`PromptNotFound`) when a Langfuse chat entry has an unsupported shape rather than silently dropping. The fetched `Prompt` carries the SDK `Prompt` object under `observability_entities['langfuse_prompt']` so the existing Generation → Prompt link in the Langfuse observer fires automatically.
2833

2934
### Changed (breaking, pre-1.0)
3035

@@ -33,18 +38,20 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The
3338

3439
### Changed
3540

36-
- **Reserved-key extension** (proposal 0042, observability §3.4). Three additional bare key names — `branch_name`, `detached`, `detached_from_invocation_id` — are reserved against caller-supplied `invocation_metadata` and `set_invocation_metadata` collision; the framework rejects them at the `invoke()` boundary and at the mid-invocation augmentation helper with `ValueError`. The reserved-name set grows from 21 to 24. These three are top-level Langfuse metadata keys the observer mapping already writes; without reservation a caller key matching one would silently shadow the OA-emitted field.
41+
- **Reserved-key extension** (proposal 0042, observability §3.4, spec v0.34.0). Three additional bare key names — `branch_name`, `detached`, `detached_from_invocation_id` — are reserved against caller-supplied `invocation_metadata` and `set_invocation_metadata` collision; the framework rejects them at the `invoke()` boundary and at the mid-invocation augmentation helper with `ValueError`. The reserved-name set grows from 21 to 24. These three are top-level Langfuse metadata keys the observer mapping already writes; without reservation a caller key matching one would silently shadow the OA-emitted field. Maintenance extension of the 0041 mechanism.
42+
- **Langfuse-emitted top-level metadata key collision reservation** (proposal 0041, observability §3.4, spec v0.31.0). The reserved caller-metadata key set extends to cover every OA-emitted top-level key the §8.4 Langfuse mapping writes alongside caller keys (20 names — `correlation_id`, `entry_node`, `spec_version`, `namespace`, `step`, `attempt_index`, `fan_out_index`, `subgraph_name`, etc.). Whole-key exact match; rejected at the `invoke()` boundary independently of which backend is attached. Prevents a caller key from silently overwriting an OA-emitted field in Langfuse's flat top-level metadata. Pre-existing `openarmature.*` / `gen_ai.*` prefix reservation unchanged.
3743
- **`observation.metadata.detached: true` moves to the parent-side dispatching observation** (proposal 0042, observability §8.4.2). The Langfuse mapping previously emitted `detached: true` on the dispatch observation inside the detached child trace; the §8.4.2 row added by 0042 places it on the **parent-side** dispatching observation that fires the detached child (the link observation in the main trace for detached subgraphs; the parent fan-out node observation for detached fan-outs). The detached-side observation no longer carries the flag.
3844
- **`LangfuseClient.update_trace` Protocol grows `input` / `output` keyword parameters** so observer-supplied values land on the Trace's headline fields.
3945

4046
### Fixed
4147

48+
- **`install_log_bridge` no-ops when an OTel logging handler is already attached** to the root logger by an SDK that auto-installs one (e.g., HyperDX). The previous attach-always behavior produced duplicate `LoggingHandler` instances and double-emitted log records when used alongside such an SDK. The installer now detects an existing `LoggingHandler` whose provider matches the current `LoggerProvider` and skips the re-attach.
4249
- **`InvocationCompletedEvent.final_state` on the failure path now surfaces the partial state at failure point.** Spec §8.4.1 *Resume semantics* requires the failure-path `trace.output` hook to receive "the partial final state captured at the failure point"; the original PR #99 implementation defaulted to `starting_state`, so the hook saw pre-execution state when it should have seen post-execution-up-to-failure state. The engine now tracks the latest post-merge state via a `latest_state_box` on `_InvocationContext`, updated after every successful step and read on the failure path. Success-path behavior unchanged.
4350
- **`latest_state_box` is per-context, not shared across subgraph descents.** Unlike the sibling `final_node_box` (which shares by reference because the spec wants the innermost failing node's name — the real culprit), `latest_state_box` must isolate per level so the outermost Langfuse trace receives outer-state-typed values. Without the isolation, a subgraph-internal step's inner-typed state would leak up to the outer trace.output hook, breaking the hook's typed contract. Each subgraph / fan-out instance / parallel-branches branch gets its own fresh box. Pinned by four regression tests covering flat, subgraph, fan-out, and parallel-branches failure paths.
4451

4552
### Notes
4653

47-
- **Pinned spec version bumped from v0.31.0 to v0.36.0.** Absorbs proposals 0042 (reserved-key extension), 0043 (Langfuse trace.input/output sourcing), 0044 (parallel-branches OTel dispatch span), and the textual additions in v0.32.0 (Gemini wire-format mapping, 0038, not yet implemented) and v0.33.0 (sessions capability, 0020, not yet implemented).
54+
- **Pinned spec version bumped from v0.27.1 to v0.38.0 over the v0.11.0 cycle.** Eight proposals absorbed: 0039 (caller-supplied invocation_id, v0.32.0), 0040 (mid-invocation open-span metadata update, v0.31.0), 0041 (Langfuse top-level metadata key collision reservation, v0.31.0), 0042 (reserved-key extension to 24 names, v0.34.0), 0043 (Langfuse trace input/output sourcing, v0.35.0), 0044 (parallel-branches OTel dispatch span, v0.36.0), 0045 (nested-lineage augmentation containment scope, v0.37.0), and 0046 (multi-message chat-prompt rendering, v0.38.0). The pinned spec also carries the textual additions in v0.32.0 (Gemini wire-format mapping, 0038, not yet implemented) and v0.33.0 (sessions capability, 0020, not yet implemented).
4855
- `LangfuseSDKAdapter` now applies `trace.input` / `trace.output` to the live Langfuse Trace. Input lands on the first real observation under the trace via `set_trace_io`; output uses a synthetic short-lived `openarmature.trace_io` observation as the carrier. The InMemoryLangfuseClient used by tests applies the fields directly.
4956
- Conformance fixture `observability/conformance/037-langfuse-trace-input-output` activated for all five cases (default stub / `disable_state_payload=False` / hooks non-null / hooks null-fallthrough / resume re-fire). The langfuse harness grew per-case `checkpointer: in_memory` wiring, a compact `flaky:` test seam, and a two-phase resume-flow assertion path.
5057
- The Langfuse v4 SDK marks `set_current_trace_io` / `Span.set_trace_io` deprecated ("removal in a future major version"). Empirical verification against Langfuse Cloud (v4.7.1, 2026-05-29) confirms it remains the **only** path that populates the Traces list view's headline `Input` / `Output` columns; `propagate_attributes(metadata=...)` does not substitute for it in the current UI. We will revisit when Langfuse publishes a concrete migration guide for v5.

docs/concepts/observability.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ A walk-through:
153153
instance contains a parallel-branches node). The combination
154154
`(namespace, branch_name, fan_out_index, attempt_index, phase)`
155155
uniquely identifies each event source. On the OTel mapping
156-
side, an `openarmature.branch_name` span attribute is added in
157-
parallel to the existing `openarmature.node.fan_out_index`.
156+
side, an `openarmature.node.branch_name` span attribute is added
157+
in parallel to the existing `openarmature.node.fan_out_index`.
158158

159159
## Routing errors and the completed event
160160

docs/concepts/prompts.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,35 @@ manager = PromptManager(backend, jinja_undefined=jinja2.Undefined)
115115
templates that walk nested attributes. Reach for these only when the
116116
strict default is actively wrong for your workflow.
117117

118+
## Two variants: text and chat
119+
120+
`Prompt` is a discriminated union over `TextPrompt` and `ChatPrompt`:
121+
122+
- A `TextPrompt` carries a single `template: str` and renders to
123+
exactly one `UserMessage`. This is the simpler variant and the
124+
default for the filesystem backend; reach for it when the prompt
125+
is a single user instruction and you don't need role tagging.
126+
- A `ChatPrompt` carries `chat_template: list[ChatSegment]`. Each
127+
segment is either a `ContentSegment` (a role-tagged content
128+
block — `system`, `user`, or `assistant`, carrying a text
129+
template OR a list of content-block templates for multimodal
130+
user messages) or a `PlaceholderSegment` (a slot the caller fills
131+
at render time with a `list[Message]`, useful for chat history
132+
injection).
133+
134+
`PromptManager.render(prompt, variables, placeholders=...)`
135+
dispatches on the variant. For `TextPrompt` the `placeholders`
136+
kwarg is ignored. For `ChatPrompt` each content segment renders
137+
with the strict-undefined rule applied independently; placeholder
138+
segments inject their caller-supplied message lists in order.
139+
140+
Backends can return either variant — the `LangfusePromptBackend`
141+
maps Langfuse text prompts to `TextPrompt` and Langfuse chat
142+
prompts to `ChatPrompt` with one `ContentSegment` per Langfuse
143+
chat message. Discriminate at the call site with
144+
`isinstance(prompt, ChatPrompt)` when you need variant-specific
145+
behavior; most callers just pass the prompt back into `render()`.
146+
118147
## Per-prompt sampling parameters
119148

120149
A `Prompt` carries an optional `sampling` field — a `SamplingConfig`

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "openarmature"
7-
version = "0.10.0"
7+
version = "0.11.0rc1"
88
description = "Workflow framework for LLM pipelines and tool-calling agents."
99
readme = "README.md"
1010
requires-python = ">=3.12"

src/openarmature/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# OpenArmature — Agent documentation
22

3-
*This is the agent guide bundled with the openarmature Python package, version 0.10.0 (spec v0.38.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.11.0rc1 (spec v0.38.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`.*
44

55
## TL;DR
66

src/openarmature/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@
2424
sessions opening the project find the bundled docs automatically.
2525
"""
2626

27-
__version__ = "0.10.0"
27+
__version__ = "0.11.0rc1"
2828
__spec_version__ = "0.38.0"

tests/test_smoke.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010
def test_package_versions() -> None:
11-
assert openarmature.__version__ == "0.10.0"
11+
assert openarmature.__version__ == "0.11.0rc1"
1212
assert openarmature.__spec_version__ == "0.38.0"
1313

1414

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)