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

## [Unreleased]

### Added

- **`locate_artifacts` and `ArtifactLocation`**: `pipelex_sdk.artifacts.locate_artifacts(value)` is the artifact walk with its paths — every `pipelex-storage://` reference in a JSON value, deduplicated in discovery order exactly as `collect_artifacts` returns them, each as an `ArtifactLocation` whose `found_at` lists every `$`-rooted path at which it sits (`$.rooms[3].staged_photo.url`, `$.items[0].url`, `$["a key"].url`), written the way `@pipelex/sdk`'s `locateArtifacts` writes them.

### Changed

- **`download_artifacts` names each file after the field it fills, and `artifact_filename` takes a location (Breaking)**: a saved file is named after the first path at which its reference sits — `$.rooms[3].staged_photo.url` is saved as `rooms-3-staged_photo.png`, and an output that is one image as `main_stuff.png` — instead of after the last segment of its storage key, which now supplies only the extension, so the same run saves under the same names from either SDK. `artifact_filename(location, content_type, scope)` replaces `artifact_filename(uri, content_type, index)` and raises `ArtifactOperationError` for anything but an `ArtifactLocation` whose first path is in the walk's notation; a field whose name Windows reserves for a device (`aux`, `nul`, `com1` and the like) is saved with a trailing `_` (`aux_.png`); the full rule is on `docs/artifact-download.md`.
- **`DownloadedArtifact` carries a required `found_at` (Breaking)**: every item of a `download_artifacts` verdict carries its reference's `found_at`, on the saved arm and the error arm alike, so a file that was not saved still says which field it would have filled. `DownloadedArtifact` is now an `ArtifactLocation`, so `artifact_filename` takes a verdict item as it is, and code that builds `DownloadedArtifact` values — a test fake standing in for `download_artifacts` — must now supply the field.

## [v0.11.0] - 2026-09-23

### Added
Expand Down
4 changes: 2 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,9 +257,9 @@ The wire models are snake_case Pydantic v2. Response models are extension-open (

## Artifact stack (`pipelex_sdk/artifacts.py` + `pipelex_sdk/artifact_models.py`)

The download twin of input preparation, and the Python twin of `@pipelex/sdk`'s `src/artifacts.ts`: `collect_artifacts` walks a value for `pipelex-storage://` references, `resolve_artifacts` mints fresh links for a whole list through the bulk route (chunked at its bound), `fetch_artifact` yields one bounded stream, and `download_artifacts` saves a run's produced files under a directory as a produced verdict. The operations take a `Protocol` rather than the client, so `PipelexAPIClient` satisfies them structurally and a test injects a fake; the client also carries all four as methods, the way it carries `upload_file` and `prepare_inputs`. The whole contract, the options and the error taxonomy are in [artifact-download.md](./artifact-download.md).
The download twin of input preparation, and the Python twin of `@pipelex/sdk`'s `src/artifacts.ts`: `locate_artifacts` walks a value for `pipelex-storage://` references and says every `$`-rooted path each one sits at, `collect_artifacts` is the same walk's bare references, `resolve_artifacts` mints fresh links for a whole list through the bulk route (chunked at its bound), `fetch_artifact` yields one bounded stream, and `download_artifacts` saves a run's produced files under a directory as a produced verdict, each file named after the field it fills by `artifact_filename`'s rule. The operations take a `Protocol` rather than the client, so `PipelexAPIClient` satisfies them structurally and a test injects a fake; the client also carries `resolve_artifacts`, `fetch_artifact` and `download_artifacts` as methods, the way it carries `upload_file` and `prepare_inputs`. The whole contract, the options and the error taxonomy are in [artifact-download.md](./artifact-download.md).

**The shapes are owned by `pipelex_sdk/artifact_models.py`** — the scope enum, the wire items, the options and the verdict, plus the public defaults — beside `product_models` and `crate_models`. That split is not cosmetic: `pipelex_sdk/errors.py` types `ScopeUnavailableError.scope` and `ArtifactAuthenticationError.verdict` with two of them and the operations module imports those errors, so keeping the shapes in the operations module would be an import cycle (`reportImportCycles` is an error here). The JS twin needs no such split, TypeScript tolerating the cycle.
**The shapes are owned by `pipelex_sdk/artifact_models.py`** — the scope enum, the wire items, the location a walk answers, the options and the verdict, plus the public defaults — beside `product_models` and `crate_models`. That split is not cosmetic: `pipelex_sdk/errors.py` types `ScopeUnavailableError.scope` and `ArtifactAuthenticationError.verdict` with two of them and the operations module imports those errors, so keeping the shapes in the operations module would be an import cycle (`reportImportCycles` is an error here). The JS twin needs no such split, TypeScript tolerating the cycle.

**The object store is fetched on its own httpx client**, never the API client's: no `Authorization` header of ours can ride along to the store, redirects are refused rather than followed, and the call's timeout is both a budget over the whole exchange and httpx's per-operation timeout. `_new_storage_client` is the one seam the module opens, which is where the unit suite injects an `httpx.MockTransport`.

Expand Down
Loading
Loading