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
* Refresh README and docs homepage pitch
Replace the README's 10-bullet "Why OpenArmature" feature inventory
with 5 differentiating reasons-to-choose, ordered for an engineer or
architect evaluating OA against alternatives:
1. Workflows to agents, one engine (LLM-infused pipelines first;
agents and deterministic ETL as the spectrum endpoints).
2. Crash-safe resume by spec contract (built for preemptible
compute and queue workers, not just human-in-the-loop interrupts).
3. Destination-pluggable observability (native OTel for vendor
neutrality, separate native LangfuseObserver for teams on
Langfuse, no SaaS lock-in to a framework-vendor-owned product).
4. Bad graphs don't compile (.compile rejects six categories of
structural error before invoke is reachable).
5. Spec, not just code (public language-agnostic spec with
conformance fixtures bounding the future surprise space).
Match the same positioning on docs/index.md's homepage card grid.
Swap the six feature-inventory cards (typed-state, compile-time,
observable, checkpointable, fan-out, async/LLM-agnostic) for six
reason-to-choose cards mirroring the README order, with a sixth
card capturing parallelism (fan-out + parallel-branches + nested
attribution correctness, formalized) that's genuinely
differentiated against LangGraph's Send-based primitive.
Existing card icons retained. "Open specification" section stays at
the bottom of the homepage.
* Tighten pitch wording per PR review
Three CoPilot findings on PR #114:
1. docs/index.md card 5 used "sub-graphs"; the canonical spelling
across both code and docs is "subgraphs" (no hyphen). Normalized.
2. docs/index.md card 4 and README.md card 4 both claimed .compile()
rejects "undeclared subgraph fields". The actual error
(MappingReferencesUndeclaredField) fires for both side='parent'
AND side='subgraph' across the fan_out.{items_field, target_field,
collect_field, inputs, extra_outputs} checks; the previous wording
was too narrow. Reworded to "mappings to undeclared state fields"
so both surfaces match each other and the actual error shape.
3. CHANGELOG bullet framed the change as "around 5 reasons-to-choose"
then enumerated 6 homepage cards. Reworded the bullet to
acknowledge both counts (5 in README; 5 + retained async-first
card on the homepage = 6).
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,6 +8,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The
8
8
9
9
### Changed
10
10
11
+
-**README and docs homepage refreshed around reasons-to-choose.** Replaced the 10-bullet "Why OpenArmature" feature inventory in `README.md` with 5 differentiating reasons (LLM-infused workflows to agents on one engine; crash-safe resume by contract; destination-pluggable observability with OTel + Langfuse, no SaaS lock-in; compile-time topology checks; spec + conformance). The docs homepage (`docs/index.md`) card grid carries the same five plus a sixth card retained from the previous grid for async-first / LLM-agnostic: workflows-to-agents, crash-safe, pluggable observability, bad-graphs-don't-compile, parallelism (fan-out + parallel-branches + nested correctness), async-first.
11
12
-**Docs sweep: stale references and em-dash normalization.** Fixed three definite stale references (`spec_version='0.26.0'` in the Langfuse example output now reads `'0.38.0'`; the dangling `v0.16.1` qualifier dropped from the parallel-branches concept page; `compiled.attach_observer` corrected to `graph.attach_observer` in `non-obvious-shapes.md` for variable-name consistency with the rest of the docs). Swept em dashes out of the user-facing docs (130 instances across 17 files) per the convention set during the patterns expansion. mkdocs strict build clean; no broken intra-docs links.
State schemas are frozen Pydantic models. Nodes return partial updates; the engine merges. The snapshot a node holds can't change mid-execution, and assignment into state raises rather than silently writing.
29
+
**One framework, from LLM-infused workflows to tool-calling agents.**<br>
30
+
OpenArmature is built for LLM pipelines first: extract, classify, route, render, validate, persist, with the LLM dropped in wherever probability beats hand-written rules. Tool-calling agents (graphs that cycle back to an LLM node) and pure deterministic ETL (no LLM at all) sit at the two ends of the same spectrum. The graph engine has zero concept of LLMs, tools, or messages; those live at the node boundary behind a `Provider` Protocol. One platform for the whole gradient, instead of bending agent-shaped frameworks to fit workflow-shaped work.
31
31
32
-
**Schema validation at every merge.**<br>
33
-
Fields outside the declared schema fail at the merge boundary instead of silently dropping. A node returning `{"plann": "..."}` (typo) raises `StateValidationError` immediately, not three nodes downstream when the field is read and doesn't exist.
32
+
**Crash-safe resume is first-class, by spec contract.**<br>
33
+
Every completed node is followed by a synchronous checkpoint save before the engine advances. Any node fails, the process dies, OOM kill, preemption: the next `invoke(resume_invocation=...)` picks up from the last saved state with a fresh `invocation_id` (audit trail) and the original `correlation_id` preserved (cross-system join). Explicit state-schema migration registration handles old in-flight checkpoints when the schema evolves. Built for preemptible compute, queue workers, and any environment where the process can die mid-step, not just for human-in-the-loop interrupt resume.
34
34
35
-
**Merge policy on the schema, not the call site.**<br>
36
-
Each state field declares its reducer (`last_write_wins`, `append`, `merge`, or a user-defined callable) as part of the schema. Two nodes writing the same field compose via the field's policy: once, declaratively, instead of duplicated across call sites.
35
+
**Destination-pluggable observability, not anchored to a paid SaaS.**<br>
36
+
`OTelObserver` (in `openarmature[otel]`) emits the OpenTelemetry GenAI semantic conventions (`gen_ai.system`, `gen_ai.request.*`, `gen_ai.response.*`, `gen_ai.usage.*`) that Honeycomb, HyperDX, Phoenix, Datadog APM, Tempo, an open-source Jaeger, or your own OTLP collector all render natively without per-service shims. On top of that, a separate `LangfuseObserver` (in `openarmature[langfuse]`) provides a native mapping for teams who've chosen Langfuse: MIT-licensed, self-hostable, decoupled through a `LangfuseClient` Protocol so swapping it out is a single-file change. No coupling to a closed-source product owned by the framework vendor.
37
37
38
-
**Subgraphs compose with explicit data seams.**<br>
39
-
Subgraphs run against their own state schema with `inputs` (additive, opt in to share parent fields) and `outputs` (replacement, name exactly what comes back) mappings. Parent fields don't leak in by accident; subgraph fields don't slip out unless declared.
38
+
**The graph either compiles or it never runs.**<br>
39
+
`.compile()` rejects six categories of structural error before `invoke()` is reachable: unreachable nodes, dangling edges, conflicting reducers, no declared entry, mappings to undeclared state fields, multiple outgoing edges from one node. State schemas are frozen Pydantic models validated at every merge boundary. For a 30-node pipeline with conditional routing, the difference between "tests pass" and "tests pass on today's code path" is structural.
40
40
41
-
**Bad graphs don't compile.**<br>
42
-
Dangling edges, unreachable nodes, conflicting reducers, no declared entry, mappings to undeclared fields, multiple outgoing edges from one node. Six categories of structural error all fail at `.compile()`, not at runtime mid-execution. The graph either constructs cleanly or it doesn't reach `invoke()`.
41
+
**There's a spec, not just code.**<br>
42
+
OpenArmature is defined by a public, language-agnostic [specification](https://github.com/LunarCommand/openarmature-spec) with conformance fixtures every reference implementation must pass. Behavior is bounded by the spec; implementations conform to it. Minor-version surprises around state merge, fan-out collection, or resume semantics live in proposals tracked openly, not in silent code changes between releases.
43
43
44
-
**The graph engine has no concept of LLMs or tools.**<br>
45
-
Validation, retry, recovery, structured output: those are node-internal or middleware concerns. The same engine runs deterministic ETL pipelines and tool-calling agents; the topology layer doesn't pick a side.
46
-
47
-
**Determinism is a contract.**<br>
48
-
Same input, same node implementations, same edge functions, same final state, and same observed node-execution order. The spec mandates it; conformance fixtures verify it across every implementation. Replay an audit run and get byte-identical state.
49
-
50
-
**Checkpoint saves are synchronous-by-contract.**<br>
51
-
The engine awaits each save before advancing. A crash immediately after a `completed` event cannot have lost the corresponding write. Resume mints a fresh `invocation_id` (audit trail) while preserving `correlation_id` (cross-system join key), so a recovered run is traceable as a new attempt without losing the thread to the original request.
52
-
53
-
**Observability that doesn't double-export.**<br>
54
-
The OpenTelemetry mapping mandates a private `TracerProvider`. That prevents the trap where global-provider auto-instrumentation libraries (OpenInference, Langfuse v3, etc.) emit duplicate spans alongside the framework's. Your spans flow exactly where you point them; no surprise fan-out to vendor backends you didn't configure.
55
-
56
-
**LLM spans LLM-aware backends can actually read.**<br>
57
-
Each `provider.complete()` call emits a dedicated `openarmature.llm.complete` span carrying both the framework's `openarmature.llm.*` attributes and the cross-vendor OpenTelemetry GenAI semantic conventions (`gen_ai.system`, `gen_ai.request.*`, `gen_ai.response.*`, `gen_ai.usage.*`). Langfuse, Phoenix, Honeycomb's LLM lens — they render generations correctly out of the box, no per-service attribute-mapping shim required. Input/output payload emission is opt-in (`disable_llm_payload=False`), default-off because the payload may contain PII; image bytes are unconditionally redacted at the provider so they never enter the observability stream.
58
-
59
-
**Native Langfuse mapping, not just OTLP.**<br>
60
-
Alongside the OpenTelemetry mapping, `LangfuseObserver` (in `openarmature[langfuse]`) maps invocations to Langfuse Traces and Observations directly — subgraph hierarchy, per-instance fan-out, and detached-trace mode included. Both observers can run on one graph. Caller-supplied invocation metadata (`invoke(metadata={"tenantId": ...})`) propagates to every backend at once: `openarmature.user.*` span attributes on the OTel side, top-level `trace.metadata` / `observation.metadata` keys on the Langfuse side.
44
+
For the full feature catalog see [openarmature.ai/concepts](https://openarmature.ai/concepts/).
0 commit comments