Release v0.10.1 - #37
Merged
Merged
Conversation
…as two shapes (#31) `MethodData.mthds` has two at-rest shapes and the field does not say which it holds — the catalog `[{name, content}]` array the webapp editor writes, or a bare `.mthds` bundle as plain text — so a Python caller holding one had to guess, and guessing wrong sends the two characters `[]` to the runner as MTHDS source. `method_source_to_contents` resolves it to the `list[str]` that `run`, `start` and `validate` take as `mthds_contents`, bringing the Python SDK to the contract `@pipelex/sdk`'s `methodSourceToContents` already holds. It reads the array the way the server reads the same row: the catalog form is recognized on key presence, and an entry whose `content` is not a non-blank string is dropped while its siblings are kept — character for character what the platform's resolver and `@pipelex/sdk` do. A client-side reader of a server-stored field exists so a caller can do locally what the platform does with that row, so the two must not disagree. The decoder is shared with `parse_method_files`, which keeps reading `MethodData.python` strictly, and now converts the JSON decoder's `RecursionError` into the `ValueError` the contract documents. Closes L-260907-28adb9 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds `method_source_to_contents` to resolve the two at-rest shapes of `MethodData.mthds` — the catalog `[{name, content}]` array or a bare `.mthds` bundle — into the `list[str]` that `run`, `start`, and `validate` take as `mthds_contents`, so a caller no longer has to guess which shape a stored row holds or send `[]` to the runner on a wrong guess. Also converts the JSON decoder's `RecursionError` into `ValueError` in `parse_method_files`, so a stored `python` nested too deeply now fails as a `pydantic.ValidationError` instead of escaping `get_method` past a caller's `except ValidationError`. - Treats an array as the catalog form on key presence and drops entries whose `content` is not a non-blank string, keeping siblings — matching the platform's own resolver and `@pipelex/sdk` exactly; the earlier stricter reading was ruled against because it disagreed with the code that decides whether a stored method runs. - Shares only the decoder and blankness rules with `parse_method_files`, never raises, and reads a `None` field as "no source". Closes L-260907-28adb9. <sup>Written for commit 9f152bc. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/Pipelex/pipelex-sdk-python/pull/31?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…docs page, Python (#33) `RunResults` now declares `graph_assembly_error`, the three I/O artifacts (`pipe_io_contracts`, `input_form`, `output_form`, typed by import from `mthds.protocol` as closed shapes) and `pipe_io_artifacts_error`, and the blocking path lifts the executed graph and unwraps the runner's artifact envelope onto the same fields the hosted path relays, so every field reads the same whichever path ran. A new `docs/run-results.md` walks each field, with the `model_fields_set` reading that keeps a key the hosted body did not carry distinct from one relayed as null. Closes L-260918-f1a51b Closes L-260921-c9d9d9 Advances L-260921-dbd611 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013EHNErfCtpGRo7RJDNgRdC <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic `RunResults` now carries the executed graph and a run's data description on both the hosted and blocking paths, so every field reads the same whichever path ran. Closes L-260918-f1a51b and L-260921-c9d9d9. - Declares `graph_assembly_error`, the three I/O artifacts (`pipe_io_contracts`, `input_form`, `output_form`, typed from `mthds.protocol`) and `pipe_io_artifacts_error`; the blocking path lifts each off `pipe_output`, unwrapping the runner's `pipe_io_artifacts` envelope onto the three sibling fields. - Fixes the blocking path dropping the executed graph — `start_and_wait` now lifts `pipe_output.graph_spec` instead of writing `None`. - Adds `docs/run-results.md`, covering each field and how a Python reader distinguishes an absent key from one relayed as `null` via `model_fields_set`. - The I/O artifacts are closed shapes: a member the pinned `mthds` does not define fails the parse of the whole results body with pydantic's `ValidationError`, and on the blocking path a failed parse discards the runner's response. <sup>Written for commit 329f848. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/Pipelex/pipelex-sdk-python/pull/33?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…ython twin (#34) `summarize_usage` folds a completed run's `tokens_usages` / `usage_assembly_error` pair into one `UsageSummary`, the Python twin of the JS fold, with the empty-list reading of ruling D6 and a per-pipe rollup. A body that never carried the key raises `FieldNotIncludedError`, which Python can tell from a relayed null, and the record's cost is now validated strict and finite so a coerced or non-finite value fails the parse instead of reaching a total. Closes L-260918-f9bc27 Advances L-260921-dbd611 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_013EHNErfCtpGRo7RJDNgRdC <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic `summarize_usage` folds a completed run's `tokens_usages` / `usage_assembly_error` pair into one run-level `UsageSummary` — state, priced USD total with a partial-cost flag, input/output token totals, call count, relayed assembly error, and a per-pipe rollup ordered most expensive first. - Pure function: no I/O, no client, input untouched. - An empty record list reads as a run that did no inference (cost `0`, zero tokens); `None` stays reserved for unavailable or unrated calls. - A results body that never carried `tokens_usages` raises the new `FieldNotIncludedError` instead of reading as unavailable, since `model_fields_set` tells an absent key from a relayed `null`. - `TokensUsageRecord.cost` is now validated strict and finite, so a coerced or non-finite value fails the parse rather than reaching a total. <sup>Written for commit 78ba23b. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/Pipelex/pipelex-sdk-python/pull/34?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…hon twin (#35) The Python artifact stack, the twin of the JS one over httpx and `asyncio`: `collect_artifacts` walks a run's results for storage references, `resolve_artifacts` signs them through the bulk route in chunks, `fetch_artifact` streams one within bounds, and `download_artifacts` saves a scope's files under a byte budget with typed per-item errors and a verdict. The live e2e leg is written and gated on credentials this branch did not have, so it skips here. Closes L-260918-f3342c Advances L-260921-dbd611 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Adds the Python artifact stack, the download twin of `prepare_inputs`: `collect_artifacts` walks a run's results for `pipelex-storage://` references offline, `resolve_artifacts` mints fresh links via the bulk route in chunks, `fetch_artifact` streams one reference under a byte cap, and `download_artifacts` saves a scope's files under a byte budget with a typed per-item verdict. Links are always minted fresh, no client credentials reach the object store, files are never overwritten, and partial files are unlinked on failure or cancellation. It mirrors `@pipelex/sdk`'s 0.18.0 contract. Closes L-260918-f3342c and advances L-260921-dbd611. **Migration** - Bare `pytest` and `make test` now collect only `tests/unit`; the live e2e leg runs via `make e2e-test` and skips itself without `PIPELEX_E2E_BASE_URL` and `PIPELEX_API_KEY`. <sup>Written for commit aea0099. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/Pipelex/pipelex-sdk-python/pull/35?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
…, Python twin (#36) `RunResults` now declares `working_memory`, typed as the standard's `DictWorkingMemoryAbstract` imported from `mthds`, so a completed run's whole memory reads the same on both paths: the hosted path relays the `working_memory.json` artifact as its own key instead of leaving it on `model_extra`, and the blocking path lifts it off `pipe_output.working_memory`. The download stack's `working_memory` scope reads the declared field, and the run-results, architecture and artifact-download pages record the run-results surface at parity with `@pipelex/sdk`. Closes L-260918-7c9a2c Advances L-260921-dbd611 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Declares `working_memory` on `RunResults`, typed as the standard's `DictWorkingMemoryAbstract` imported from `mthds`, so a completed run's whole memory reads the same whichever path ran. - The hosted path relays the `working_memory.json` artifact as its own key instead of leaving it on `model_extra`; the blocking path lifts it off `pipe_output.working_memory`, which the standard declares required. - `download_artifacts`'s `working_memory` scope now reads the declared field, and the run-results, architecture, and artifact-download pages track the surface at parity with `@pipelex/sdk`. Closes L-260918-7c9a2c. <sup>Written for commit d1893d3. Summary will update on new commits.</sup> <a href="https://cubic.dev/pr/Pipelex/pipelex-sdk-python/pull/36?utm_source=github" target="_blank" rel="noopener noreferrer" data-no-image-dialog="true"><picture><source media="(prefers-color-scheme: dark)" srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img alt="Review in cubic" src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a> <!-- End of auto-generated description by cubic. --> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Release v0.10.1
Bumps version from
0.10.0to0.10.1. Promotesdev→main: the Python run-results parity wave —working_memory,summarize_usage, the graph and I/O lifts with their docs page, the artifact stack and the bulk storage resolve — plus the stored-source reader and two fixes.Changelog
Added
RunResults.working_memory, the run's whole memory declared on both paths. A completed run now hands back every named stuff it held when it finished — the inputs it was given, the intermediates it produced and the main output — on one field that reads the same whichever path ran, typed as the standard'sDictWorkingMemoryAbstractimported frommthdsrather than restated:rootmaps a stuff name onto itsconceptandcontent,aliasesmaps a role such asmain_stuffonto one of those names, and every level stays extension-open so a runner's per-stuff extras survive. On the hosted path it is theworking_memory.jsonartifact the platform relays as its own key, no longer an unnamed value ridingmodel_extra; on the blocking path it is lifted offpipe_output.working_memory, which the standard declares required, so it is always set there. TheNone-versus-absent reading the other optional fields follow applies to it too, anddownload_artifactswalks the declared field for itsworking_memoryscope. This closes the last run-results gap against@pipelex/sdk. Seedocs/run-results.md.summarize_usage, one run-level reading of what a run consumed.pipelex_sdk.usage.summarize_usage(results)folds a completed run'stokens_usages/usage_assembly_errorpair into a singleUsageSummary— astateofrecords,no_inferenceorunavailable, the priced total in USD withcost_partialflagging a sum that mixes priced and unrated calls, the two additive token totals, the call count, the relayed assembly error and aby_piperollup ordered most expensive first, with the calls the runtime did not attribute grouped under aNonepipe code. Every ruledocs/run-usage.mdstates is applied in one place instead of re-derived by each consumer: an unrated call (costofNone) is kept apart from one priced at zero, onlyinputandoutputare summed because the other categories are subsets, and an empty record list is a run that did no inference — a cost of0and zero tokens, neverNone. It is pure, so it needs no client and does no I/O. A results body that never carriedtokens_usagesraises the newFieldNotIncludedErrorrather than turning a key the read did not deliver into a run with no usage to show; a key relayed asnullis a value and reads asunavailable.TokensUsageRecord.costis now validated strict and finite, so a cost relayed as a string, a bool or a NaN fails the results body's parse instead of reaching a total as a number. This is the Python twin of@pipelex/sdk'ssummarizeUsage, with the same summary shape.RunResultsdescribes a run's graph and its data. Besidegraph_specit already carried, the results object now declaresgraph_assembly_error— the graph's twin ofusage_assembly_error, non-Nonewhen the runner's graph assembly failed, which is the only thing that tells a broken graph apart from a run that produced none — and the three I/O artifacts that say what the graph's nodes hold:pipe_io_contracts,input_formandoutput_form, typed as the standard'sPipeIOContracts,InputFormandOutputFormimported frommthds.protocolexactly as the validate report types them, built over the library the run executed against and keyed by namespacedpipe_ref, withpipe_io_artifacts_errorbeside them as their own "why is there none". Every one reads the same on both paths: the hosted results body relays each as an artifact, and the blocking path lifts each offpipe_output, unwrapping the runner'spipe_io_artifactsenvelope onto the three sibling fields. The artifacts are closed shapes, so a member the pinnedmthdsdoes not define fails the parse of the whole results body — the same ruling the validate report follows. The hosted body relays neither error key yet, so on that path each is absent frommodel_fields_setrather thanNone, and that is the Python reading of the JSundefined. This is the Python half of what@pipelex/sdkshipped in 0.18.0 and 0.20.0.docs/run-results.md— "Reading a run's results", a page walking every field ofRunResults: how an absent key differs from a relayednullin Python, the run id as the durable handle,main_stuffwith a workedget_run_resultexample, whatgraph_specis and how to keep it, the graph and artifact error twins, the three I/O artifacts and the rule that the contracts and the output form are taken together, the usage pair,pipe_output, and the produced files whose signedpublic_urlmust not be stored. Linked fromdocs/architecture.md, whose parity section now names what the run-results surface still lacks against the JS SDK.method_source_to_contents, the reader for a stored method's bundle source:pipelex_sdk.product_models.method_source_to_contents(method.mthds)turns the polymorphic stringget_methodhands back — the catalog[{name, content}]array the webapp editor writes, or a bare.mthdsbundle as plain text — into thelist[str]thatrun,startandvalidatetake asmthds_contents. It never raises, and an empty list means the method carries no source rather than that reading it failed. It reads the array as the catalog form on key presence and then drops an entry whosecontentis not a non-blank string, keeping its siblings — the same reading the platform's own resolver and@pipelex/sdkapply to the same stored row, so one stored method reads the same way wherever it is read.pipelex_sdk.artifactscarries the download twin ofprepare_inputs, in four layers a consumer can stop at:collect_artifacts(value)lists thepipelex-storage://references inside any results value without touching the network,resolve_artifacts(uris)mints a fresh link for each through the platform's bulk route (POST /v1/resolve-storage-url/bulk, chunked at its bound, one verdict per reference with per-reference refusal as a value),fetch_artifact(uri)yields one bounded stream (a timeout, redirects refused, the byte cap enforced mid-stream, no credential of ours forwarded to the object store, the store's headers left neutral), anddownload_artifacts(run_id | results, dir_path=…)saves a whole run's files under a directory — by run id days after the run, or from aRunResultsin hand — and answers a produced verdict naming every reference walked, errors included. Links are always minted fresh and never read off the content's expiringpublic_url, files are never overwritten, a partial file is unlinked on failure or cancellation, and theworking_memoryscope brings down the echoed inputs and intermediates too. The shapes, options and defaults live inpipelex_sdk.artifact_models, the typed failures inpipelex_sdk.errors(ArtifactOperationErrorand its subclasses, plusFieldNotIncludedError), and the whole contract isdocs/artifact-download.md. This is the Python twin of what@pipelex/sdkshipped in 0.18.0.resolve_storage_urls_bulk, one request for a whole list of storage references. The client gained the raw bulk wire call under the artifact stack:POST /v1/resolve-storage-url/bulktakes at most the route's bound of references and answers one item per reference in request order, duplicates included, a refused reference carrying its{code, detail}inside a200. A run producing one file per page no longer costs one round trip per file, andresolve_storage_urlstays for the caller with a single link to mint.Fixed
start_and_waitnow liftspipe_output.graph_specontoRunResults.graph_specinstead of writingNone— the runner has always returned the graph there — so the field carries the same document whichever path ran.ValidationError:parse_method_filesconverts the JSON decoder'sRecursionErrorinto theValueErrorits contract documents, soMethodData's validator surfaces it as apydantic.ValidationErrorlike any other malformed response body instead of letting a bareRecursionErrorescapeget_methodpast a caller'sexcept ValidationError.Closes L-260922-d9bfb5
🤖 Generated with Claude Code
Summary by cubic
Releases v0.10.1, promoting the run-results parity wave to main.
RunResultsnow declares the run's whole memory, its graph and I/O artifacts, and thesummarize_usagefold; the artifact stack downloads produced files through a new bulk storage route; and a stored method's source can be read back into the shape the runner expects.New Features
RunResults.working_memoryreturns the run's full memory on both paths, and the graph and I/O artifacts (graph_assembly_error,pipe_io_contracts,input_form,output_form) now read the same whichever path ran.summarize_usagefolds a run's usage records into oneUsageSummarywith a priced total and per-pipe rollup; a body that never carriedtokens_usagesraisesFieldNotIncludedError.collect_artifacts,resolve_artifacts,fetch_artifact,download_artifacts) saves a run's produced files to disk, with fresh links minted through the new bulkresolve_storage_urls_bulkroute.method_source_to_contentsturns a storedMethodData.mthds— the catalog array or a plain.mthdsbundle — into themthds_contentslist thatrun,startandvalidatetake.docs/run-results.mdanddocs/artifact-download.mddocument the new surfaces.Bug Fixes
graph_specoffpipe_outputinstead of writingNone.ValidationErrorinstead of a bareRecursionError.Closes L-260922-d9bfb5.
Written for commit d135454. Summary will update on new commits.