The Python client for the Pipelex hosted API.
pipelex-sdk is the Python counterpart of @pipelex/sdk, exactly as mthds (the mthds-python package) is the Python counterpart of the mthds npm package. It is the hosted superset: the five normative MTHDS Protocol routes (inherited from mthds) plus the durable run lifecycle plus the Pipelex product surface (methods, organizations, billing, API keys, onboarding, storage, run records).
One-way dependency: pipelex-sdk → mthds.
pip install pipelex-sdkThe API key resolves, in order: explicit api_key argument → PIPELEX_API_KEY → anonymous. The token is optional — anonymous access works against the protocol routes (e.g. a local bare runner); the product routes return 401.
The base URL resolves, in order: explicit base_url argument → PIPELEX_BASE_URL → the hosted default https://api.pipelex.com. The base URL is host-only (no path/query/fragment); every endpoint composes as {base}/v1/{endpoint}.
The SDK never reads the mthds resolver (MTHDS_API_KEY / MTHDS_BASE_URL / ~/.mthds/config) — those settings configure the vendor-neutral mthds tooling and whichever runner it targets, not this Pipelex client.
request_timeout_seconds (constructor argument, default 20 min) sets the per-instance blocking-execute ceiling the inherited protocol routes (execute / start / validate / models / version) use.
The client is async-only (httpx AsyncClient) and is an async context manager.
from pipelex_sdk.client import PipelexAPIClient
from pipelex_sdk.validation_models import VALIDATION_VIEW_INPUT_FORM
async def main() -> None:
async with PipelexAPIClient() as client:
# 1. Validate an MTHDS bundle. The verdict is always returned (never raised):
# a 200 discriminated on `is_valid`, carrying `rendered_markdown`.
# Structured views are opt-in: asking for `input_form` here is what populates
# `report.input_form` (the per-pipe input-form descriptors); omit `views` and the
# request body carries no `views` key at all.
report = await client.validate([bundle_text], views=[VALIDATION_VIEW_INPUT_FORM])
print(report.rendered_markdown)
if not report.is_valid:
return
# 2. Run a method end-to-end. `start_and_wait` self-heals across runner kinds:
# durable start+poll on the hosted API, blocking execute on a bare runner.
result = await client.start_and_wait(
pipe_code="my_pipe",
inputs={"topic": "quantum computing"},
)
# 3. Read the output. Every completed run delivers a resolved `main_stuff`
# (the full working memory also rides `pipe_output` on the blocking path).
print(result.main_stuff)A method reaches every method-taking call in exactly one of three forms: inline source, a method_ref address (github.com/<owner>/<repo>[/<selector>][@<tag>], resolved by the server — pipelex-api >= 0.21.0; on api.pipelex.com availability follows the platform deploy that forwards it), or a hosted method_id (mt_…, resolved by the platform). A method_ref pairs with nothing — it is a complete run source — and its runs carry typed provenance:
ack = await client.start(method_ref="github.com/Pipelex/methods/documents@v0.1.0", inputs={...})
print(ack.method_provenance.commit_sha) # the SHA actually fetched — stable even if the tag moves
result = await client.wait_for_result(ack.pipeline_run_id)
# The tooling routes take the same selectors under a strict XOR (exactly one, no pairing):
report = await client.validate(method_ref="github.com/Pipelex/methods/documents@v0.1.0")
report = await client.validate(method_id="mt_123")Behind the hosted gateway, a synchronous execute() is cut off at ~30s and surfaces a PipelineExecuteTimeoutError pointing here. For long methods, drive the durable lifecycle yourself — the run survives client disconnects and is resumable by pipeline_run_id:
ack = await client.start(pipe_code="long_pipe", inputs={...})
result = await client.wait_for_result(ack.pipeline_run_id)The hosted product routes raise a typed ApiResponseError carrying the RFC 9457 code discriminant. Branch on err.code, which is decoupled from the transport status:
from pipelex_sdk.errors import ApiResponseError
try:
created = await client.create_pipelex_api_key(label="ci")
print(created.api_key) # plaintext — returned only once
except ApiResponseError as exc:
if exc.code == "pipelex_api_key_limit_reached":
print("Per-account key limit reached — revoke an old key first.")
else:
raiseThere is no barrel import — package __init__.py files stay empty. Import each symbol from its module:
- Client & construction —
from pipelex_sdk.client import PipelexAPIClient, DEFAULT_API_BASE_URL, MthdsFile - Run lifecycle types —
from pipelex_sdk.runs import RunStatus, RunPublic, RunRead, RunResults, RunResultState, WaitForResultOptions, PollInfo - Product wire models —
from pipelex_sdk.product_models import UserProfile, MethodData, MethodWriteInput, Membership, MembershipsResponse, SubscriptionResponse, PlanView, InvoiceView, OnboardingSubmission, UploadInput, UploadedFile, PipelineRun, ... - Validation verdict types —
from pipelex_sdk.validation_models import PipelexValidationResult, PipelexValidationReport, PipelexInvalidReport, ValidationErrorItem, SuggestedFix, VALIDATION_VIEW_INPUT_FORM, ... - Typed errors —
from pipelex_sdk.errors import ApiResponseError, ApiUnreachableError, PipelineExecuteTimeoutError, PagingNotTerminatingError, RunFailedError, RunTimeoutError, RunLifecycleUnavailableError, RunStillRunningError, ... - Version —
from pipelex_sdk.version import __version__ - Protocol surface (the MTHDS standard's wire types) comes from the
mthdsdependency — e.g.from mthds.protocol.exceptions import PipelineRequestError,from mthds.protocol.models import ValidationResult(the neutral verdict union thatPipelexValidationResultnarrows). - Input-form descriptors and pipe I/O contracts come from
mthdstoo, because they are the standard's artifacts and this SDK only carries them:from mthds.protocol.input_form import InputForm, InputFormField, ListField, TextField, ...andfrom mthds.protocol.pipe_io_contracts import PipeIOContracts, PipeInputContract, PresenceMarker, IOMultiplicity, ....PipelexValidationReport.input_formand.pipe_io_contractsare typed with them, so a node narrows on itskindand a slot's presence and multiplicity read as enums — butpipelex_sdkdoes not re-export the vocabulary, and importing it from here is the one supported path.
make install # create the venv and install all extras (resolves `mthds` from ../mthds-python)
make agent-check # fix-imports + format + lint + pyright + mypy
make agent-test # run the test suite quietly (prints only on failure)
make check # full gate: agent-check aggregate + unused-imports + pylintSee CLAUDE.md for the coding standards and docs/architecture.md for the design (including the parity map against @pipelex/sdk).
MIT — see LICENSE.