Skip to content

v0.34.0

Choose a tag to compare

@github-actions github-actions released this 08 Oct 05:11
· 24 commits to main since this release
252974e

@pipelex/sdk on npm

Highlights

A method runs from the shell, with no project around it. npx @pipelex/sdk run runs a method on the hosted API through the SDK's durable run lifecycle and prints its main output as JSON, and npx @pipelex/sdk script writes a shell script that runs it with this version pinned. pipelex-sdk on PyPI ships the same command, and both are held to one recorded table of cases.

Added

  • isGatewayCutOff: the rule the blocking execute applies to tell the hosted gateway's ~30-second cut-off from a refusal is now exported, as isGatewayCutOff(error, elapsedMs): a 503 or 504 answer, or the client's own time limit (ABORT_TIMEOUT), at least ~28 seconds after the request was sent. A caller timing its own start, from startAndWaitForResult's onStarting for one, reads it to learn that a start the gateway gave up on may still create the run, as the pipelex-sdk command does. docs/errors.md describes it.
  • signal on prepareInputs and uploadFile: the request of prepareInputs takes an optional signal, which stops the preparation between its steps, a file's read and its upload included: once it has aborted, neither the pipe I/O request nor any upload starts, and prepareInputs throws the abort. uploadFile's options take the same signal, read once the file's bytes are in hand, right before the upload request. A request already sent runs to its end. docs/input-preparation.md describes it.
  • filename on every upload failure: UploadAuthenticationError, UnsupportedUploadCapabilityError and UploadTransportError now carry filename, the file uploadFile was sending, as RejectedAssetError already did, so a consumer preparing several files says which one failed without parsing the message. uploadWithGrant leaves it undefined.
  • InvalidInputValueError and MethodLoadError: prepareInputs now tells the inputs' own mistakes from a method that does not load by class. A data: URL at a file input that does not decode, and a value of a type no file input takes, throw InvalidInputValueError, where they threw a plain InputPreparationError; a pipe I/O answer saying the method does not load (is_valid: false) throws MethodLoadError, carrying the answer's validationErrors and its own message as serverMessage, where only the first item's message reached the caller, inside the error's sentence. Both derive from InputPreparationError, keep its verdict (input, not retryable) and keep their messages, so a catch on the base is unchanged. docs/input-preparation.md lists each preparation failure.
  • The pipelex-sdk command: the package now declares a bin, so npx @pipelex/sdk run --method <address | mt_id | bundle> --inputs inputs.json runs a method on the hosted API through the SDK's durable run lifecycle and prints its main output as JSON on stdout, the run id, each uploaded file and every error going to stderr; --inputs-template prints the object --inputs takes; Ctrl-C leaves the run going and names it, and a failure that leaves a run going, or possibly created, says so. npx @pipelex/sdk script --method <address | mt_id> checks the method and writes a shell script that runs it with this SDK's version pinned. The key comes from PIPELEX_API_KEY only and no .env file is read. The command adds no runtime dependency, and the package entry does not export it. docs/cli.md describes it, and tests/fixtures/cli-cases.json records its behaviour case by case, which the Python SDK's command of the same name runs too.
  • onStarted and onStarting on startAndWaitForResult: the second argument is now StartAndWaitForResultOptions, the wait's options plus onStarted, called once with the start acknowledgement as soon as the durable run exists and before the first poll, so a caller that waits through startAndWaitForResult holds the run's id while it waits, to show it, log it or resume the run by it after an interrupt; it is never called on the blocking path, which has no run id to give. onStarting is called right before each request that may create a run is sent, the start or the blocking execute, so a caller that stops waiting before then knows that no run was started; it is never called once the signal has aborted. docs/run-results.md describes them.

Fixed

  • The blocking execute times its request on the monotonic clock: it measured the time to the hosted gateway's ~30-second cut-off with Date.now, so a change of the system's time while the request was out, such as a time sync stepping the clock, could turn a fast 503 into a PipelineExecuteTimeoutError or hide a real cut-off. It now uses performance.now, which no such change moves.
  • startAndWaitForResult sends nothing once its signal has aborted: an abort that landed while the GET /v1/version handshake was in flight stopped nothing, so the start or the blocking execute was sent once the handshake answered, and a run was created for a caller that had already given up. The signal is now read after the handshake and before the fallback to the blocking execute, and the call throws the abort without creating a run. docs/run-results.md describes it.
  • A success answer that is not UTF-8 is an answer the SDK cannot read: every route read a 2xx body as Response.text() does, a byte that is not UTF-8 turning into U+FFFD, so a JSON answer holding such a byte could hand back a mangled value, a run id included. It now throws the ApiResponseError of an answer the SDK cannot read, as the Python SDK refuses one; a refusal's body is still read leniently for its reason.
  • A start carrying an inline bundle gets the blocking ceiling: start() gave a request whose bundle was inline as mthds_contents the poll's 30-second limit, so a large bundle on a slow uplink timed out on POST /v1/start while the same payload succeeded on the blocking execute; it now gets the blocking ceiling, as a bundle in files or bundle_b64 and a method_ref already did.
  • An unanswered version handshake throws at once: a GET /v1/version that got no answer was taken for a hosted API for the client's lifetime, and startAndWaitForResult sent the start anyway; it now throws its ApiUnreachableError at once, sends no start and caches nothing, so the next call asks again. An answer that is no usable version, a body that arrived and does not decode, such as a broken gzip stream, included, is still taken for a hosted API, as the Python SDK takes it.
  • A refused base URL is no longer echoed whole: the RequestArgumentError the constructor throws for a base URL that is not host-only quoted the value verbatim, so the credentials, the path or the query token the rule exists to keep out reached logs and any page that shows an error's message. The refusal now shows the scheme and the host alone and names the parts beyond them without their text (Invalid API base URL "https://api.example.com" with credentials and a query (not shown): …), and a value that is not an http or https URL is not shown at all.

pipelex-sdk on PyPI

Highlights

A method runs from the shell, with no project around it. uvx pipelex-sdk run runs a method on the hosted API through the SDK's durable run lifecycle and prints its main output as JSON, and uvx pipelex-sdk script writes a shell script that runs it with this version pinned. @pipelex/sdk on npm ships the same command, and both are held to one recorded table of cases. Every route now reports a missing answer the same way: a request that gets no answer raises ApiUnreachableError, whose code tells a read or write timeout from a failure to connect.

Added

  • is_gateway_cut_off: the rule the blocking execute applies to tell the hosted gateway's ~30-second cut-off from a refusal is now public, as pipelex_sdk.client.is_gateway_cut_off(exc, elapsed_seconds): a 503 or 504 answer, or the client's own read or write timeout (ABORT_TIMEOUT), at least ~28 seconds after the request was sent. A caller timing its own start, from start_and_wait's on_starting for one, reads it to learn that a start the gateway gave up on may still create the run, as the pipelex-sdk command does. docs/architecture.md describes it.
  • The pipelex-sdk command: the package now declares a console script, so uvx pipelex-sdk run --method <address | mt_id | bundle> --inputs inputs.json runs a method on the hosted API through the SDK's durable run lifecycle and prints its main output as JSON on stdout, the run id, each uploaded file and every error going to stderr; --inputs-template prints the object --inputs takes; Ctrl-C leaves the run going and names it, and a failure that leaves a run going, or possibly created, says so. uvx pipelex-sdk script --method <address | mt_id> checks the method and writes a shell script that runs it with this SDK's version pinned (exec uvx pipelex-sdk@X.Y.Z run …). The key comes from PIPELEX_API_KEY only and no .env file is read. The command adds no dependency, and nothing in the library imports it. It is the twin of @pipelex/sdk's command of the same name, which it shares no code with: both run every case of one recorded table, tests/fixtures/cli-cases.json, so they take the same flags and print the same lines. docs/cli.md describes it, and make test-package runs the built wheel's command through uvx in CI.
  • InvalidInputValueError and MethodLoadError: prepare_inputs now tells the inputs' own mistakes from a method that does not load by class. A data: URL at a file input that does not decode, and a value of a type no file input takes, raise InvalidInputValueError, where they raised a plain InputPreparationError; a pipe I/O answer saying the method does not load (is_valid: false) raises MethodLoadError, carrying the answer's validation_errors and its own message as server_message, where only the first item's message reached the caller, inside the error's sentence. Both derive from InputPreparationError and keep their messages, so an except on the base is unchanged. They are the twins of @pipelex/sdk's classes of the same names; docs/input-preparation.md lists each preparation failure.
  • filename and status on upload failures: UnsupportedUploadCapabilityError, UploadAuthenticationError and UploadTransportError now carry filename, the file upload_file was sending, as RejectedAssetError already did, and UploadTransportError carries the answer's status when one came back, so a caller preparing several files says which one failed without parsing the message. They are the twins of @pipelex/sdk's fields; docs/input-preparation.md lists them.
  • on_started and on_starting on start_and_wait: on_started takes a callable that is called once with the start acknowledgement, a PipelexRunResultStart, as soon as the durable run exists and before the first poll, so a caller that waits through start_and_wait holds the run's id while it waits, to show it, log it or resume the run by it after cancelling the wait; it is never called on the blocking path, which has no run id to give. on_starting takes a callable with no argument, called right before each request that may create a run is sent, the start or the blocking execute, so a caller that stops waiting before then knows that no run was started. An exception either raises propagates before the next request. They are the twins of @pipelex/sdk's onStarted and onStarting; docs/run-results.md describes them.

Changed

  • A connection or pool timeout keeps its own code (Breaking): ApiUnreachableError.code is ABORT_TIMEOUT only for a read or write timeout, a request that reached the API and outlasted its time limit; a timeout while connecting, or while waiting for a pooled connection, now carries ConnectTimeout or PoolTimeout, where every timeout carried ABORT_TIMEOUT, and a blocking execute that could not connect is no longer taken for one the gateway cut off. A caller that branched on ABORT_TIMEOUT for every timeout branches on the httpx names too.
  • pipe_io sends only what a request sets: the body of POST /v1/pipe-io now leaves out every field at its default as well as every None, so the route gets what @pipelex/sdk sends for the same call and applies the same defaults.

Fixed

  • execute, start, validate, models and version raise ApiUnreachableError when no answer comes back (Breaking): a refused connection, a DNS failure, a TLS failure or a timeout on the protocol routes the client inherits from mthds escaped as httpx's own exception, outside PipelineRequestError, while the run reads and the product routes already raised ApiUnreachableError. Every route now raises it, start_and_wait included, so a caller that caught httpx.TransportError or httpx.TimeoutException from these routes catches ApiUnreachableError and reads its code (ABORT_TIMEOUT for a read or write timeout, the httpx failure's name otherwise). A blocking execute cut off by a timeout after about 28 seconds still raises PipelineExecuteTimeoutError, whose cause is now that ApiUnreachableError. A body httpx cannot decode, such as a broken gzip stream, raises it too, with the code DecodingError, where httpx's DecodingError escaped, as @pipelex/sdk reports one (Z_DATA_ERROR).
  • A refused base URL is no longer echoed whole: the PipelineRequestError the constructor raises for a base URL that is not host-only quoted the value verbatim, so the credentials, the path, its ; parameters or the query token the rule exists to keep out reached logs and any page that shows an error's message. The refusal now shows the scheme and the host alone and names the parts beyond them without their text (Invalid API base URL "https://api.example.com" with credentials and a query (not shown): it must be host-only …), and a value that is not an http or https URL is not shown at all, word for word as @pipelex/sdk refuses it.
  • InvalidLocalSourceError names the system's error code: a local file that input preparation cannot read is now reported as Local file cannot be read: "<path>" (ENOENT)., naming the code (ENOENT, EACCES, …) where it named the Python exception class, as @pipelex/sdk reports it.
  • The version handshake and a plain start no longer wait as long as a blocking run: version(), the handshake of start_and_wait, and a start sending neither a bundle nor a method_ref waited request_timeout_seconds for an answer, the blocking execute's ceiling by default; they now have request_timeout_seconds capped at the poll's per-request limit, the hosted gateway's own cut-off, so a caller who set a shorter limit to fail fast keeps it, while a start carrying a bundle, inline as mthds_contents or as files or bundle_b64, or a method_ref, which the server resolves before it answers, keeps request_timeout_seconds. A handshake that gets no answer now raises its ApiUnreachableError at once, sends no start and caches nothing, so the next call asks again, where it was taken for a hosted API for the client's lifetime; one answered with a body that is not JSON or not UTF-8, such as a gateway's HTML page, which escaped as json.JSONDecodeError or UnicodeDecodeError and sent no start, is now taken for a hosted API, as an error status, a body that is no version and a body that does not decode already were, and as @pipelex/sdk takes them.
  • A cancelled start_and_wait or upload_file sends nothing more: a task cancelled while the version handshake's answer was read could go on to send the start or the blocking execute, and one cancelled while upload_file read and encoded a file could go on to upload it; each now raises asyncio.CancelledError before that request, as @pipelex/sdk stops on an aborted signal.
  • typing-extensions 4.4.0 or later: the package imports typing_extensions.override, which first shipped in 4.4.0, yet declared typing-extensions>=4.0.0, so an environment holding an older release failed at import. It now requires 4.4.0 or later.
  • wait_for_result stops when its task is cancelled, on Python 3.11 too: on Python 3.11, cancelling the task awaiting wait_for_result or start_and_wait in the loop step where a poll answered was swallowed, and the wait polled on until the run ended or timeout_seconds ran out. It now raises asyncio.CancelledError on every supported Python version, so an asyncio.timeout around the wait, a cancelled request handler or Ctrl-C under asyncio.run stops it.

@pipelex/create-method-app on npm, with the method-app templates

Changed

  • The web app template runs on @pipelex/sdk 0.33.0, with config_invalid in place of config_missing (Breaking): bumped from 0.30.0. The config_missing kind classified ClientAuthenticationError, which the SDK never threw and no longer exports; config_invalid now classifies a PIPELEX_BASE_URL naming more than a host, which the SDK refuses before sending any request and the app used to report as "Something went wrong". A 2xx whose body the SDK cannot read is shown as "Pipelex API sent an answer the app could not read", with the raw body in the technical details, where it used to be an unexplained error.
  • The web app template's rejected request or server error always says whether a re-run can help: its retry line is the SDK's verdict, the API's own retryable when its problem document sent one and otherwise the SDK's reading of the HTTP status, so a 4xx says a re-run unchanged fails the same way and a passing 5xx offers one; a server error whose type the app recognizes (missing provider credentials, no inference backend, a method definition the server rejected) stays final unless the API says otherwise. The technical details mark a member of the verdict the API did not send as the SDK's reading, and name the recognized error type whose own verdict overrides it.
  • The web app template's failed upload offers a retry where one can help: storage answering that it does not implement the upload (its 501) now reads as final instead of "usually temporary", while storage timing out or throttling the upload, and two writes colliding, are offered a retry, all following the SDK's verdict.

Fixed

  • The web app template's durable poll rides out a rate limit: a status read the API refuses with a 429 or a 408 is read again, as a gateway 5xx already was, where it used to abandon a run that was still executing; a read that cannot pass, such as a 501, gives up at once. When the poll does give up, the error no longer invites a re-run, since the run may still be executing and running it again would start a second one.

Security

  • The web app template no longer shows a refused PIPELEX_BASE_URL to its visitors: the SDK's refusal of a value naming more than a host quotes that value whole, and the app showed it in the technical details of the error every visitor sees, credentials and query included. The details now show only its scheme and host, and name the parts beyond them without their content.

The JavaScript starter, Pipelex/pipelex-starter-js

Changed

  • @pipelex/sdk 0.33.0, and config_invalid in place of config_missing (Breaking): bumped from 0.30.0. The config_missing kind classified ClientAuthenticationError, which the SDK never threw and no longer exports; config_invalid now classifies a PIPELEX_BASE_URL naming more than a host, which the SDK refuses before sending any request and the starter used to report as "Something went wrong". A 2xx whose body the SDK cannot read is shown as "Pipelex API sent an answer the app could not read", with the raw body in the technical details, where it used to be an unexplained error.
  • A rejected request or a server error always says whether a re-run can help: its retry line is the SDK's verdict, the API's own retryable when its problem document sent one and otherwise the SDK's reading of the HTTP status, so a 4xx says a re-run unchanged fails the same way and a passing 5xx offers one; a server error whose type the starter recognizes (missing provider credentials, no inference backend, a method definition the server rejected) stays final unless the API says otherwise. The technical details mark a member of the verdict the API did not send as the SDK's reading, and name the recognized error type whose own verdict overrides it.
  • A failed upload offers a retry only where one can help: storage answering that it does not implement the upload (its 501) now reads as final instead of "usually temporary", and storage timing out or throttling the upload is offered a retry, both following the SDK's verdict.

Fixed

  • A durable run's poll rides out a rate limit: a status read the API refuses with a 429 or a 408 is read again, as a gateway 5xx already was, where it used to abandon a run that was still executing; a read that cannot pass, such as a 501, gives up at once. When the poll does give up, the error no longer invites a re-run, since the run may still be executing and running it again would start a second one.

Security

  • A refused PIPELEX_BASE_URL no longer reaches the browser: the SDK's refusal of a value naming more than a host quotes that value whole, and the starter showed it in the technical details of the error every visitor sees, credentials and query included. The details now show only its scheme and host, and name the parts beyond them without their content.

The Python starter, Pipelex/pipelex-starter-python

Changed

  • pipelex-sdk 0.31.0 and mthds 0.19.0 are the floors (Breaking): bumped from 0.30.0 and 0.18.0, the mthds that both pipelex-sdk and pipelex now pin, so a project can install the two together again. mthds 0.19.0 reads a stuff's concept as its ref string, which nothing in the template reads; a project's own code that read stuff.concept.code reads stuff.concept instead.