Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to `openarmature-python` are documented in this file.

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).

## [Unreleased]

### Notes

- **Pinned spec version bumped to v0.17.1.** Proposal 0019 (multi-provider wire-format extension) reframes llm-provider §8 as a catalog of wire-format mappings, with the existing OpenAI-compatible body nested under §8.1. Purely textual on the spec side — no behavioral change, no fixture changes. Code and doc references to §8.X updated to match the new structure (§8.1 → §8.1.1, §8.2 → §8.1.2, §8.3 → §8.1.3, §8.5.1 → §8.1.5.1, §8.1.1 → §8.1.1.1). All existing conformance fixtures continue to pass.

## [0.8.0] — 2026-05-23

LLM-provider span payload and GenAI semconv release. Pinned spec
Expand Down
2 changes: 1 addition & 1 deletion openarmature-spec
Submodule openarmature-spec updated 27 files
+13 −0 CHANGELOG.md
+8 −8 README.md
+5 −2 docs/proposals.md
+1 −0 docs/proposals/0022-harness-contract.md
+1 −0 docs/proposals/0023-canonical-state-reducers.md
+2 −2 proposals/0019-llm-provider-multi-provider-extension.md
+1 −1 spec/llm-provider/conformance/001-basic-completion.md
+1 −1 spec/llm-provider/conformance/002-tool-call-roundtrip.md
+2 −2 spec/llm-provider/conformance/004-error-categories.md
+1 −1 spec/llm-provider/conformance/004-error-categories.yaml
+6 −6 spec/llm-provider/conformance/005-openai-wire-mapping.md
+2 −2 spec/llm-provider/conformance/005-openai-wire-mapping.yaml
+2 −2 spec/llm-provider/conformance/010-content-blocks-image-url.md
+1 −1 spec/llm-provider/conformance/010-content-blocks-image-url.yaml
+2 −2 spec/llm-provider/conformance/011-content-blocks-image-inline-base64.md
+1 −1 spec/llm-provider/conformance/011-content-blocks-image-inline-base64.yaml
+1 −1 spec/llm-provider/conformance/012-content-blocks-image-detail-hint.md
+1 −1 spec/llm-provider/conformance/016-content-blocks-unsupported-by-model.yaml
+1 −1 spec/llm-provider/conformance/020-content-blocks-inline-image-missing-media-type.md
+1 −1 spec/llm-provider/conformance/021-structured-output-success.md
+1 −1 spec/llm-provider/conformance/021-structured-output-success.yaml
+5 −5 spec/llm-provider/conformance/026-structured-output-openai-wire-mapping-native.md
+2 −2 spec/llm-provider/conformance/026-structured-output-openai-wire-mapping-native.yaml
+4 −4 spec/llm-provider/conformance/027-structured-output-openai-wire-mapping-fallback.md
+1 −1 spec/llm-provider/conformance/027-structured-output-openai-wire-mapping-fallback.yaml
+1 −1 spec/llm-provider/conformance/028-structured-output-no-schema-regression.md
+52 −16 spec/llm-provider/spec.md
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Repository = "https://github.com/LunarCommand/openarmature-python"
Specification = "https://github.com/LunarCommand/openarmature-spec"

[tool.openarmature]
spec_version = "0.17.0"
spec_version = "0.17.1"

[dependency-groups]
dev = [
Expand Down
2 changes: 1 addition & 1 deletion src/openarmature/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""OpenArmature: workflow framework for LLM pipelines and tool-calling agents."""

__version__ = "0.8.0"
__spec_version__ = "0.17.0"
__spec_version__ = "0.17.1"
22 changes: 11 additions & 11 deletions src/openarmature/llm/providers/openai.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Spec: realizes llm-provider §8 (concrete OpenAI provider) including
# the §8.3 wire-error mapping table.
# Spec: realizes llm-provider §8.1 (OpenAI-compatible wire-format mapping) including
# the §8.1.3 wire-error mapping table.

"""OpenAI-compatible HTTPX-based provider.

Expand Down Expand Up @@ -389,7 +389,7 @@ async def _do_complete(
return self._parse_response(cast("dict[str, Any]", payload_raw), schema_dict, schema_class)

# ------------------------------------------------------------------
# Request building (spec §8.1)
# Request building (spec §8.1.1)
# ------------------------------------------------------------------

def _build_request_body(
Expand Down Expand Up @@ -433,7 +433,7 @@ def _build_request_body(
},
}
elif not include_response_format:
# On the fallback path the §8.5.1 contract is "response_format
# On the fallback path the §8.1.5.1 contract is "response_format
# MUST NOT be on the wire." RuntimeConfig is extra="allow" so
# a caller could pass response_format through via the extras
# loop above; strip it here so the fallback contract holds
Expand All @@ -442,7 +442,7 @@ def _build_request_body(
return body

# ------------------------------------------------------------------
# Response parsing (spec §8.2)
# Response parsing (spec §8.1.2)
# ------------------------------------------------------------------

def _parse_response(
Expand All @@ -460,7 +460,7 @@ def _parse_response(
raise ProviderInvalidResponse(f"response missing required fields: {exc}") from exc
finish_reason: str = finish_reason_raw if isinstance(finish_reason_raw, str) else "error"

# Per §8.2 (and conformance fixture 005's
# Per §8.1.2 (and conformance fixture 005's
# `function_call_legacy_finish_reason_mapping` case): the
# legacy `finish_reason: "function_call"` value MUST be
# normalized to the spec's `"tool_calls"`. This is a
Expand Down Expand Up @@ -717,13 +717,13 @@ def _augment_messages_with_schema_directive(


def _message_to_wire(msg: Message) -> dict[str, Any]:
"""Spec §8.1 request mapping for one message."""
"""Spec §8.1.1 request mapping for one message."""
if isinstance(msg, SystemMessage):
return {"role": "system", "content": msg.content}
if isinstance(msg, UserMessage):
# Dual-shape user content (§8.1): string maps directly; a
# Dual-shape user content (§8.1.1): string maps directly; a
# content-block sequence maps to OpenAI's content-array form
# per §8.1.1.
# per §8.1.1.1.
if isinstance(msg.content, str):
return {"role": "user", "content": msg.content}
return {
Expand Down Expand Up @@ -760,7 +760,7 @@ def _message_to_wire(msg: Message) -> dict[str, Any]:
}


# Spec §8.1.1: content-block to OpenAI content-array entry mapping.
# Spec §8.1.1.1: content-block to OpenAI content-array entry mapping.
# Both URL-referenced and inline-base64 image blocks go through
# OpenAI's `image_url` entry shape; the inline case is expressed as
# an RFC 2397 data: URI carrying media_type + base64_data. The
Expand Down Expand Up @@ -872,7 +872,7 @@ def classify_http_error(resp: httpx.Response) -> LlmProviderError:
if status in (401, 403):
return ProviderAuthentication(message or f"HTTP {status}")
if status == 400:
# Spec §8.3: HTTP 400 bodies that indicate the bound model
# Spec §8.1.3: HTTP 400 bodies that indicate the bound model
# rejected a content block map to provider_unsupported_content_block
# rather than the generic provider_invalid_request. The
# detection rule is implementation-defined.
Expand Down
2 changes: 1 addition & 1 deletion tests/conformance/harness/directives.py
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ class MockResponse(_AllowExtras):
Permissive shape because the body's content mirrors OpenAI's wire
format which is wide and evolving; modelling every field would
duplicate the OpenAI schema. The ``llm-provider`` capability's
spec.md §8 is the authoritative shape.
spec.md §8.1 is the authoritative shape.
"""

status: int | None = None
Expand Down
2 changes: 1 addition & 1 deletion tests/conformance/test_llm_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
behavior in terms of OpenAI Chat Completions wire-format mock
responses + expected ``Provider.complete()`` / ``Provider.ready()``
outcomes. The harness drives the real :class:`OpenAIProvider` via
``httpx.MockTransport`` so the wire-mapping path (spec §8) is
``httpx.MockTransport`` so the wire-mapping path (spec §8.1) is
exercised end-to-end — fixture 005 explicitly tests that mapping, so
mocking at the Provider boundary would skip what we want to verify.

Expand Down
2 changes: 1 addition & 1 deletion tests/test_smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

def test_package_versions() -> None:
assert openarmature.__version__ == "0.8.0"
assert openarmature.__spec_version__ == "0.17.0"
assert openarmature.__spec_version__ == "0.17.1"


def test_spec_version_matches_pyproject() -> None:
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_llm_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ def test_transient_categories_excludes_terminal_categories() -> None:


# ---------------------------------------------------------------------------
# classify_http_error — wire-mapping table (spec §8.3)
# classify_http_error — wire-mapping table (spec §8.1.3)
# ---------------------------------------------------------------------------


Expand Down
Loading