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
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## [v0.9.0] - 2026-09-02

### Added

- **The validate report carries both structured views, and `validate` can ask for them.** `PipelexValidationReport` gains `input_form` and `output_form`, and `validate` / `validate_files` gain a `views` parameter with `VALIDATION_VIEW_INPUT_FORM` and `VALIDATION_VIEW_OUTPUT_FORM` beside it. Neither existed here before: `input_form` shipped in `pipelex-api` at 0.18.0 and never reached this SDK, and `output_form` followed it — so a caller had no route to the two artifacts a form or a result renderer needs.

Both are typed by importing the standard's own models (`InputForm`, `OutputForm` from `mthds.protocol`) rather than restated as opaque dicts, matching what `pipe_io_contracts` already does: one declaration per language is what makes drift impossible.

Both are `None` rather than an empty map when absent, and that distinction carries weight here where it does not on `pipe_io_contracts` — an opt-in view's absence means the request did not ask for it, not that the method has nothing to describe. `views` is sent **only** when asked, unlike `render` which this client always injects: the point of an opt-in view is that the default response stays byte-identical, and the highest-frequency callers should not pay for bytes they discard.

### Changed

- **Requires `mthds` 0.13.0 (breaking).** The pin moves from 0.11.1, which predated `mthds.protocol.output_form` entirely. The version it moves to makes `json_schema` required on `PipeOutputContract`, and the contracts are CLOSED shapes — so a verdict from a runner that predates the output payload schema now fails the parse rather than being read half-way. That closure is deliberate: version drift should be loud.

## [v0.8.0] - 2026-08-29

### Added
Expand Down
19 changes: 18 additions & 1 deletion pipelex_sdk/validation_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,26 @@

from mthds.protocol.input_form import InputForm
from mthds.protocol.models import InvalidValidationReport, ValidationDiagnostic, ValidationReport
from mthds.protocol.output_form import OutputForm
from mthds.protocol.pipe_io_contracts import PipeIOContracts
from pydantic import BaseModel, ConfigDict, Field, TypeAdapter

from pipelex_sdk._pydantic_utils import empty_list_factory_of

VALIDATION_VIEW_INPUT_FORM: Final[str] = "input_form"
"""The one `views` token the server supports today — asks for `PipelexValidationReport.input_form`.
"""A `views` token — asks for `PipelexValidationReport.input_form`.

Deliberately a constant rather than a closed enum: the request boundary is open, the server
resolves the tokens as a set and lenient-ignores the ones it does not know (never a `422`), so
a stale token must never fail a call.
"""

VALIDATION_VIEW_OUTPUT_FORM: Final[str] = "output_form"
"""A `views` token — asks for `PipelexValidationReport.output_form`.

The twin of the above on the other side of the pipe. Named as a constant for the same reason:
a caller passing the literal string gets no help from a type checker when the token changes."""


class DryRunStatus(StrEnum):
"""Per-pipe dry-run sweep outcome on `ValidatedPipeEntry.status`."""
Expand Down Expand Up @@ -359,6 +366,16 @@ class PipelexValidationReport(ValidationReport):
(`VALIDATION_VIEW_INPUT_FORM`), and an older runner emitted it unconditionally — `None` by
default is the one typing that reads a body from either runner correctly."""

output_form: OutputForm | None = None
"""Opt-in structured view: the per-pipe output-form descriptors, the twin of `input_form` on
the other side of the pipe, requested through `views: ["output_form"]`.

One `field` rather than a list of them, and no `presence` or `gating` — those are facts of a
slot a caller fills, and a result is not one. Read together with that pipe's
`output.json_schema` off `pipe_io_contracts`: the descriptor says what the result IS, the
schema names the property its payload arrives under, and a consumer holding one but not the
other is back to inferring the other from the value."""

rendered_markdown: str | None = None
"""Opt-in Pipelex-API presentation extra: the server-rendered Markdown view of the verdict,
present only when the request asked for it (`render: ["markdown"]`); absent (None) otherwise."""
Expand Down
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pipelex-sdk"
version = "0.8.0"
version = "0.9.0"
description = "The Python client for the Pipelex hosted API — the MTHDS Protocol surface plus the durable run lifecycle and the Pipelex product surface, built on the `mthds` protocol base."
authors = [{ name = "Evotis S.A.S.", email = "oss@pipelex.com" }]
maintainers = [{ name = "Pipelex staff", email = "oss@pipelex.com" }]
Expand All @@ -18,7 +18,7 @@ classifiers = [
]

dependencies = [
"mthds==0.11.1",
"mthds==0.13.0",
"pydantic>=2.10.6,<3.0.0",
"typing-extensions>=4.0.0",
"httpx>=0.23.0,<1.0.0",
Expand Down Expand Up @@ -348,6 +348,7 @@ convention = "google"
[tool.uv]
required-version = ">=0.7.2"


[tool.pylint.main]
py-version = "3.11"
reports = false
Expand Down
5 changes: 5 additions & 0 deletions tests/unit/test_validation_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,11 @@
"multiplicity": "single",
"item_count": None,
"optional": False,
# Required on the contract since the output side gained a payload
# schema. The empty schema rather than a plausible one: this fixture
# is about the ENVELOPE parsing, and a shape here would invite an
# assertion that belongs in a test about schemas.
"json_schema": {},
},
}
},
Expand Down
10 changes: 5 additions & 5 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading