From 14356f586851559768b344db19879f718ee7f955 Mon Sep 17 00:00:00 2001 From: Louis Choquel <8851983+lchoquel@users.noreply.github.com> Date: Wed, 15 Jul 2026 14:39:16 +0200 Subject: [PATCH 1/2] Release v0.14.1: bump pipelex-tools dev dependency to >=0.7.2 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01AA5fmhTMLRLTJgVmCsW6da --- CHANGELOG.md | 4 ++++ pyproject.toml | 4 ++-- uv.lock | 22 +++++++++++----------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 583fefb..4cf628d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [v0.14.1] - 2026-07-15 + +- Bumped the `pipelex-tools` dev dependency from `>=0.3.2` to `>=0.7.2`. + ## [v0.14.0] - 2026-07-15 ### Added diff --git a/pyproject.toml b/pyproject.toml index 83d20c5..b313df1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "piper" -version = "0.14.0" +version = "0.14.1" description = "Replace this with your project description" # authors = [{ name = "Your Name", email = "your.email@example.com" }] license = "MIT" @@ -50,7 +50,7 @@ piper = ["py.typed", "methods/*/main.mthds", "methods/*/inputs.template.json"] [project.optional-dependencies] dev = [ "mypy==1.19.1", - "pipelex-tools>=0.3.2", + "pipelex-tools>=0.7.2", "pyright>=1.1.411", "pytest>=9.0.3", "pytest-mock>=3.14.0", diff --git a/uv.lock b/uv.lock index 6405de4..c3497a7 100644 --- a/uv.lock +++ b/uv.lock @@ -298,22 +298,22 @@ wheels = [ [[package]] name = "pipelex-tools" -version = "0.3.3" +version = "0.7.2" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cd/14/90c60cdec26ff46dd07ae23888c14232bfde136f23a0d73603ab71364ec3/pipelex_tools-0.3.3.tar.gz", hash = "sha256:f33b34a61a2a5555350d525b90255bc1e49d1cff19a582561cd2039204f2e78c", size = 166616, upload-time = "2026-04-13T13:30:29.661Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/01/ad82d2a4dd62c9375d0d6b8740bcda46344a1fcebcd8e3fc9a373d64e5b2/pipelex_tools-0.7.2.tar.gz", hash = "sha256:afcacadd5375db6dfd20d674b99ac260226d0688b2c3acd0b32e097c211cf371", size = 194940, upload-time = "2026-07-13T17:47:56.019Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/97/df/6dbc045acd18aa25c673d52ca0c51e89f4d16fb8dedc628552c71869f0f6/pipelex_tools-0.3.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:c6186a9cc684924189517c058724fe3213d96b4d2db64e63e57d33dcb22f139b", size = 5149231, upload-time = "2026-04-13T13:30:13.808Z" }, - { url = "https://files.pythonhosted.org/packages/79/ed/82147c55751e6e52c8fc0989ed68ae535d4bc2e5d0f3deac80d0c1f5f905/pipelex_tools-0.3.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:06616a4347f14195dd7cd6e59d683eb51e042f272d773c0ee65f794c619ca584", size = 4896559, upload-time = "2026-04-13T13:30:15.727Z" }, - { url = "https://files.pythonhosted.org/packages/9e/94/589619729e094b4dd53c1422b836afdb978eacf055136aa33f9d9b6e74d5/pipelex_tools-0.3.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad71354c1d62990e57d25bd66b1afb5acb3be5fa22b022fc23e2d7188d208cd7", size = 5028749, upload-time = "2026-04-13T13:30:18.291Z" }, - { url = "https://files.pythonhosted.org/packages/fc/e3/9b23552e5714426471d767b438febf118c2f656b4d75631b75bd324ac629/pipelex_tools-0.3.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae0d1809e9910f206cbf8a84932ab6cb53495e2ab92fcd559a38e2310f0e6097", size = 5291995, upload-time = "2026-04-13T13:30:20.475Z" }, - { url = "https://files.pythonhosted.org/packages/9c/a8/a32d2db2843e046c77d5dec291faed34d9adb25709291e491ff9c55e1388/pipelex_tools-0.3.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:737e0aeb638f9bae0f839742e24c32d1a03dc79729b07d04a5f6d20248e3722b", size = 5291875, upload-time = "2026-04-13T13:30:22.778Z" }, - { url = "https://files.pythonhosted.org/packages/66/68/e87bd2fa590a7f39f63d00f89e073ec08cdbcb254ed0da12d0b1aae9f6d7/pipelex_tools-0.3.3-py3-none-win32.whl", hash = "sha256:78bbe4ec88fabcc1f8eefadad9f37e6004ef26334f4f2665436f39bc1112ef75", size = 4671977, upload-time = "2026-04-13T13:30:25.043Z" }, - { url = "https://files.pythonhosted.org/packages/52/e3/1e6d861871ec7d9a541026e21b351052a53535328f89263efc1184bfafe8/pipelex_tools-0.3.3-py3-none-win_amd64.whl", hash = "sha256:17a45f827a4aa0adb2fc1fbbce476baa59579a8f4d2c232ddb18af72bb9655cf", size = 5467831, upload-time = "2026-04-13T13:30:27.938Z" }, + { url = "https://files.pythonhosted.org/packages/3b/5c/31a17a5ccc0528c24f9566b1274184608a3ac65e54463ba797647df4f222/pipelex_tools-0.7.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:8adbdabc3056cbe3fc235f1c0cf2d48e2d93afdc45867fb40f9f935177416aa1", size = 5177818, upload-time = "2026-07-13T17:47:41.012Z" }, + { url = "https://files.pythonhosted.org/packages/74/5e/bb1804d0cb4f5a297e66214bff32094c972acd404597db9f6c390a1acadb/pipelex_tools-0.7.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5884c135a9ac5af65ba1ae1033fa90319d6b2ebe5f54019e7386515f8624f70b", size = 4932368, upload-time = "2026-07-13T17:47:43.632Z" }, + { url = "https://files.pythonhosted.org/packages/13/17/9259a1d84fa9a69332ccbc26e87c7fbdb069f6deec2e697471e4ca6b3045/pipelex_tools-0.7.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa7014c83ffe694fef04090bf96a106669f95e07f3bd4adc330ed93a3c0f9862", size = 5004948, upload-time = "2026-07-13T17:47:45.598Z" }, + { url = "https://files.pythonhosted.org/packages/6b/26/406e3bd031cd29497f6c24a10c9e0d461821ae0c1a5f28fb512558d26abe/pipelex_tools-0.7.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ff5ef79de1945815bf2ff3686baa3e88c9cb62adaf58c23aef8fe16f7c232311", size = 5242365, upload-time = "2026-07-13T17:47:47.464Z" }, + { url = "https://files.pythonhosted.org/packages/88/c1/ea8076deae82233dd29cebf45784970eea520316c8ada93969d55f146204/pipelex_tools-0.7.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5728f735cfe3acda6052aa64749cdac74161780db60ee6152aa8b8585c39c0f1", size = 5258268, upload-time = "2026-07-13T17:47:49.488Z" }, + { url = "https://files.pythonhosted.org/packages/83/23/fe30a83212adcd30974a6f69a1c86e523112db95bf262731dd5120e61165/pipelex_tools-0.7.2-py3-none-win32.whl", hash = "sha256:a85c5b24c847c02f21f907badbc1659342bf04eeb0c7024166cc4e5ce7baa754", size = 4707280, upload-time = "2026-07-13T17:47:51.551Z" }, + { url = "https://files.pythonhosted.org/packages/f3/a9/9884212fa19e1a3272310d5de826a4d15799b10e88f2626e3990ed276c28/pipelex_tools-0.7.2-py3-none-win_amd64.whl", hash = "sha256:6b9df7166d69c5718974b1c1a7c14667c0e41943bf037ecfdf291cca71907b32", size = 5507110, upload-time = "2026-07-13T17:47:53.559Z" }, ] [[package]] name = "piper" -version = "0.14.0" +version = "0.14.1" source = { editable = "." } dependencies = [ { name = "httpx" }, @@ -339,7 +339,7 @@ requires-dist = [ { name = "httpx", specifier = ">=0.27.0" }, { name = "mypy", marker = "extra == 'dev'", specifier = "==1.19.1" }, { name = "pipelex-sdk", specifier = ">=0.4.0" }, - { name = "pipelex-tools", marker = "extra == 'dev'", specifier = ">=0.3.2" }, + { name = "pipelex-tools", marker = "extra == 'dev'", specifier = ">=0.7.2" }, { name = "pyright", marker = "extra == 'dev'", specifier = ">=1.1.411" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.3" }, { name = "pytest-asyncio", marker = "extra == 'dev'", specifier = ">=0.24.0" }, From fedbd2bcb77596642cad2ff8de498c4880867b19 Mon Sep 17 00:00:00 2001 From: Louis Choquel <8851983+lchoquel@users.noreply.github.com> Date: Wed, 15 Jul 2026 15:16:39 +0200 Subject: [PATCH 2/2] Support multi-file .mthds bundles in the mode lifecycle helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change execute_pipe / start_and_wait / start_pipe to take mthds_contents: list[str] (one entry per .mthds file) instead of a single bundle: str, passing it straight through to the SDK. A multi-file bundle cannot be concatenated into one TOML string (duplicate top-level keys), so the list — which the SDK's mthds_contents already is — is the interface. The three demos stay single-file (mthds_contents=[bundle]); broaden the package-data glob to methods/*/*.mthds so multi-file bundles ship. Docs and changelog updated. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01AA5fmhTMLRLTJgVmCsW6da --- CHANGELOG.md | 1 + README.md | 4 ++-- docs/cli-architecture.md | 2 ++ piper/attended/cli.py | 18 ++++++++++-------- piper/blocking/cli.py | 18 ++++++++++-------- piper/detached/cli.py | 15 ++++++++------- pyproject.toml | 2 +- tests/e2e/test_extract_entities.py | 2 +- tests/e2e/test_generate_image.py | 2 +- tests/e2e/test_summarize_pdf.py | 2 +- tests/unit/test_bootstrap_script.py | 2 +- 11 files changed, 38 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cf628d..ab76c57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## [v0.14.1] - 2026-07-15 +- **Multi-file bundle support in the mode lifecycle helpers:** `execute_pipe`, `start_and_wait`, and `start_pipe` now take `mthds_contents: list[str]` (the bundle's `.mthds` files as strings — one entry for a single-file bundle, several for a multi-file one) instead of a single `bundle: str`, passed straight through to the SDK. Multi-file bundles cannot be concatenated into one string (duplicate top-level TOML keys), so the list is the interface. The three demos stay single-file (`mthds_contents=[bundle]`); a method dir with several `.mthds` files is read with `[p.read_text() for p in sorted((METHODS_DIR / "").glob("*.mthds"))]`. The package-data glob broadens to `methods/*/*.mthds` so multi-file bundles ship. - Bumped the `pipelex-tools` dev dependency from `>=0.3.2` to `>=0.7.2`. ## [v0.14.0] - 2026-07-15 diff --git a/README.md b/README.md index f374394..1e8a135 100644 --- a/README.md +++ b/README.md @@ -155,8 +155,8 @@ flowchart TD classDef terminal fill:#f8fafc,stroke:#475569,stroke-width:1.5px,color:#0f172a ``` -1. **Read the bundle.** `piper` reads `methods/extract-entities/main.mthds` from disk and constructs a `PipelexAPIClient`, which picks up `PIPELEX_BASE_URL` / `PIPELEX_API_KEY` from the environment. -2. **Run it on the API.** The bundle is sent as *content* (`mthds_contents`), so nothing method-specific needs to live in the runtime — edit the `.mthds` file and re-run, no redeploy. +1. **Read the bundle.** `piper` reads `methods/extract-entities/main.mthds` from disk and constructs a `PipelexAPIClient`, which picks up `PIPELEX_BASE_URL` / `PIPELEX_API_KEY` from the environment. A method dir may hold a single `main.mthds` or several `.mthds` files (a multi-file bundle split across pipes) — read them all with `[p.read_text() for p in sorted((METHODS_DIR / "").glob("*.mthds"))]`. +2. **Run it on the API.** The bundle's files are sent together as *content* (`mthds_contents`, one string per file), so nothing method-specific needs to live in the runtime — edit the `.mthds` file(s) and re-run, no redeploy. 3. **Narrow the result.** The SDK resolves the run's `main_stuff`; the command validates it into the generated `ExtractedEntities` model (`ExtractedEntities.model_validate(main_stuff)`), printed as JSON. The typed models are **not hand-written**: they are generated from the `.mthds` bundles by `pipelex codegen` into `piper/generated/` (stamped, with a `codegen.lock` per method). Edit a bundle → `make codegen` regenerates the models and input templates → `make codegen-check` verifies offline that nothing is stale or hand-edited. See [docs/codegen.md](docs/codegen.md). diff --git a/docs/cli-architecture.md b/docs/cli-architecture.md index 051432c..ccf5295 100644 --- a/docs/cli-architecture.md +++ b/docs/cli-architecture.md @@ -44,6 +44,8 @@ All three have the same four-part shape, so they diff cleanly: 3. **The lifecycle helper** — one public async function that *is* the mode (`detached` adds the run-id lifecycle helpers described below). It gets a public name because it is the featured code, and it is what the unit tests patch and the e2e tests call directly. 4. **The demo commands + a private `_run()`** — each command reads its input, reads its bundle, awaits the lifecycle helper through `_run()` (`asyncio.run` + the single `except (PipelineRequestError, httpx.HTTPStatusError)` that presents via `piper/errors.py`), narrows the result into its *generated* model, prints JSON. +The lifecycle helpers take the bundle as `mthds_contents: list[str]` — one string per `.mthds` file — and pass it straight to the SDK. The three demos are single-file methods, so each reads its `main.mthds` and wraps it as `mthds_contents=[bundle]`. A method dir may instead hold several `.mthds` files (a multi-file bundle split across pipes with `signature_for` cross-file declarations); read them all with `[p.read_text() for p in sorted((METHODS_DIR / "").glob("*.mthds"))]` and hand that list to the helper unchanged. Concatenating the files into one string would be invalid TOML — the list is the interface for exactly this reason. + | Mode | Lifecycle helper | SDK calls | What the demo prints | | --- | --- | --- | --- | | `blocking` | `execute_pipe()` | `client.execute` | the result, as JSON | diff --git a/piper/attended/cli.py b/piper/attended/cli.py index 32cde60..41802d8 100644 --- a/piper/attended/cli.py +++ b/piper/attended/cli.py @@ -44,15 +44,17 @@ progress_console = Console(stderr=True) -async def start_and_wait(*, pipe_code: str, bundle: str, inputs: dict[str, Any]) -> Any: +async def start_and_wait(*, pipe_code: str, mthds_contents: list[str], inputs: dict[str, Any]) -> Any: """The whole attended lifecycle: start a durable run, print its id, wait here for the result. - Credentials come from `PIPELEX_API_KEY` / `PIPELEX_BASE_URL`. The SDK resolves the - method's main output for you: `.main_stuff` is the content the pipe named as its - result (a completed run that names none raises `MissingMainStuffError`). + Credentials come from `PIPELEX_API_KEY` / `PIPELEX_BASE_URL`. `mthds_contents` is the + bundle's `.mthds` files as strings — one entry for a single-file bundle, several for a + multi-file one. The SDK resolves the method's main output for you: `.main_stuff` is + the content the pipe named as its result (a completed run that names none raises + `MissingMainStuffError`). """ async with PipelexAPIClient() as client: - start_result = await client.start(pipe_code=pipe_code, mthds_contents=[bundle], inputs=inputs) + start_result = await client.start(pipe_code=pipe_code, mthds_contents=mthds_contents, inputs=inputs) run_id = start_result.pipeline_run_id # Printed before the first poll, so Ctrl-C leaves you with a usable run id. progress_console.print(f"Run started: [bold]{run_id}[/bold]") @@ -81,7 +83,7 @@ def extract_entities( if resolved.is_sample: progress_console.print(f"[dim]No text given — using the sample: {resolved.text!r}. Pass your own as an argument or via --file.[/dim]") bundle = (METHODS_DIR / "extract-entities" / "main.mthds").read_text() - main_stuff = _run(start_and_wait(pipe_code="extract_entities", bundle=bundle, inputs={"text": resolved.text})) + main_stuff = _run(start_and_wait(pipe_code="extract_entities", mthds_contents=[bundle], inputs={"text": resolved.text})) # Narrow into the generated typed model (validates the concept's shape), then print it as JSON. entities = ExtractedEntities.model_validate(main_stuff) output_console.print_json(data=entities.model_dump()) @@ -100,7 +102,7 @@ def summarize_pdf( progress_console.print(f"[dim]No file given — using the sample: {document.name}. Pass a path to summarize your own document.[/dim]") bundle = (METHODS_DIR / "summarize-pdf" / "main.mthds").read_text() inputs = {"document": build_document_input(document)} - main_stuff = _run(start_and_wait(pipe_code="summarize_pdf", bundle=bundle, inputs=inputs)) + main_stuff = _run(start_and_wait(pipe_code="summarize_pdf", mthds_contents=[bundle], inputs=inputs)) summary = DocumentSummary.model_validate(main_stuff) output_console.print_json(data=summary.model_dump()) @@ -120,7 +122,7 @@ def generate_image( if resolved.is_sample: progress_console.print(f"[dim]No prompt given — using the sample: {resolved.text!r}. Pass your own as an argument or via --file.[/dim]") bundle = (METHODS_DIR / "generate-image" / "main.mthds").read_text() - main_stuff = _run(start_and_wait(pipe_code="generate_image", bundle=bundle, inputs={"image_prompt": resolved.text})) + main_stuff = _run(start_and_wait(pipe_code="generate_image", mthds_contents=[bundle], inputs={"image_prompt": resolved.text})) # On the hosted path the runtime returns a storage `url` (`pipelex-storage://…`) # *and* a web-renderable `public_url` (a signed URL); the model keeps both. image = Image.model_validate(main_stuff) diff --git a/piper/blocking/cli.py b/piper/blocking/cli.py index 7ffe85f..a7107e8 100644 --- a/piper/blocking/cli.py +++ b/piper/blocking/cli.py @@ -36,16 +36,18 @@ progress_console = Console(stderr=True) -async def execute_pipe(*, pipe_code: str, bundle: str, inputs: dict[str, Any]) -> Any: +async def execute_pipe(*, pipe_code: str, mthds_contents: list[str], inputs: dict[str, Any]) -> Any: """The whole blocking lifecycle: one call, and the result comes back in the response. - Credentials come from `PIPELEX_API_KEY` / `PIPELEX_BASE_URL`. The SDK resolves the - method's main output for you: `.main_stuff` is the content the pipe named as its - result (a completed run that names none raises `MissingMainStuffError`). + Credentials come from `PIPELEX_API_KEY` / `PIPELEX_BASE_URL`. `mthds_contents` is the + bundle's `.mthds` files as strings — one entry for a single-file bundle, several for a + multi-file one. The SDK resolves the method's main output for you: `.main_stuff` is + the content the pipe named as its result (a completed run that names none raises + `MissingMainStuffError`). """ async with PipelexAPIClient() as client: with progress_console.status("Running…"): - result = await client.execute(pipe_code=pipe_code, mthds_contents=[bundle], inputs=inputs) + result = await client.execute(pipe_code=pipe_code, mthds_contents=mthds_contents, inputs=inputs) return result.main_stuff @@ -59,7 +61,7 @@ def extract_entities( if resolved.is_sample: progress_console.print(f"[dim]No text given — using the sample: {resolved.text!r}. Pass your own as an argument or via --file.[/dim]") bundle = (METHODS_DIR / "extract-entities" / "main.mthds").read_text() - main_stuff = _run(execute_pipe(pipe_code="extract_entities", bundle=bundle, inputs={"text": resolved.text})) + main_stuff = _run(execute_pipe(pipe_code="extract_entities", mthds_contents=[bundle], inputs={"text": resolved.text})) # Narrow into the generated typed model (validates the concept's shape), then print it as JSON. entities = ExtractedEntities.model_validate(main_stuff) output_console.print_json(data=entities.model_dump()) @@ -78,7 +80,7 @@ def summarize_pdf( progress_console.print(f"[dim]No file given — using the sample: {document.name}. Pass a path to summarize your own document.[/dim]") bundle = (METHODS_DIR / "summarize-pdf" / "main.mthds").read_text() inputs = {"document": build_document_input(document)} - main_stuff = _run(execute_pipe(pipe_code="summarize_pdf", bundle=bundle, inputs=inputs)) + main_stuff = _run(execute_pipe(pipe_code="summarize_pdf", mthds_contents=[bundle], inputs=inputs)) summary = DocumentSummary.model_validate(main_stuff) output_console.print_json(data=summary.model_dump()) @@ -98,7 +100,7 @@ def generate_image( if resolved.is_sample: progress_console.print(f"[dim]No prompt given — using the sample: {resolved.text!r}. Pass your own as an argument or via --file.[/dim]") bundle = (METHODS_DIR / "generate-image" / "main.mthds").read_text() - main_stuff = _run(execute_pipe(pipe_code="generate_image", bundle=bundle, inputs={"image_prompt": resolved.text})) + main_stuff = _run(execute_pipe(pipe_code="generate_image", mthds_contents=[bundle], inputs={"image_prompt": resolved.text})) # On the hosted path the runtime returns a storage `url` (`pipelex-storage://…`) # *and* a web-renderable `public_url` (a signed URL); the model keeps both. image = Image.model_validate(main_stuff) diff --git a/piper/detached/cli.py b/piper/detached/cli.py index 78d08a9..cca9b11 100644 --- a/piper/detached/cli.py +++ b/piper/detached/cli.py @@ -42,14 +42,15 @@ progress_console = Console(stderr=True) -async def start_pipe(*, pipe_code: str, bundle: str, inputs: dict[str, Any]) -> str: +async def start_pipe(*, pipe_code: str, mthds_contents: list[str], inputs: dict[str, Any]) -> str: """The whole detached lifecycle: start a durable run, return its id, don't wait. - Credentials come from `PIPELEX_API_KEY` / `PIPELEX_BASE_URL`. The run keeps - executing server-side after this process exits. + Credentials come from `PIPELEX_API_KEY` / `PIPELEX_BASE_URL`. `mthds_contents` is the + bundle's `.mthds` files as strings — one entry for a single-file bundle, several for a + multi-file one. The run keeps executing server-side after this process exits. """ async with PipelexAPIClient() as client: - start_result = await client.start(pipe_code=pipe_code, mthds_contents=[bundle], inputs=inputs) + start_result = await client.start(pipe_code=pipe_code, mthds_contents=mthds_contents, inputs=inputs) return start_result.pipeline_run_id @@ -93,7 +94,7 @@ def extract_entities( if resolved.is_sample: progress_console.print(f"[dim]No text given — using the sample: {resolved.text!r}. Pass your own as an argument or via --file.[/dim]") bundle = (METHODS_DIR / "extract-entities" / "main.mthds").read_text() - run_id = _run(start_pipe(pipe_code="extract_entities", bundle=bundle, inputs={"text": resolved.text})) + run_id = _run(start_pipe(pipe_code="extract_entities", mthds_contents=[bundle], inputs={"text": resolved.text})) _print_run_id(run_id) @@ -110,7 +111,7 @@ def summarize_pdf( progress_console.print(f"[dim]No file given — using the sample: {document.name}. Pass a path to summarize your own document.[/dim]") bundle = (METHODS_DIR / "summarize-pdf" / "main.mthds").read_text() inputs = {"document": build_document_input(document)} - run_id = _run(start_pipe(pipe_code="summarize_pdf", bundle=bundle, inputs=inputs)) + run_id = _run(start_pipe(pipe_code="summarize_pdf", mthds_contents=[bundle], inputs=inputs)) _print_run_id(run_id) @@ -128,7 +129,7 @@ def generate_image( if resolved.is_sample: progress_console.print(f"[dim]No prompt given — using the sample: {resolved.text!r}. Pass your own as an argument or via --file.[/dim]") bundle = (METHODS_DIR / "generate-image" / "main.mthds").read_text() - run_id = _run(start_pipe(pipe_code="generate_image", bundle=bundle, inputs={"image_prompt": resolved.text})) + run_id = _run(start_pipe(pipe_code="generate_image", mthds_contents=[bundle], inputs={"image_prompt": resolved.text})) _print_run_id(run_id) diff --git a/pyproject.toml b/pyproject.toml index b313df1..f14e3f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ packages = [ include-package-data = true [tool.setuptools.package-data] -piper = ["py.typed", "methods/*/main.mthds", "methods/*/inputs.template.json"] +piper = ["py.typed", "methods/*/*.mthds", "methods/*/inputs.template.json"] # Ship each codegen.lock alongside its models.py so `pipelex codegen check` works on an installed copy. "piper.generated.extract_entities" = ["codegen.lock"] "piper.generated.generate_image" = ["codegen.lock"] diff --git a/tests/e2e/test_extract_entities.py b/tests/e2e/test_extract_entities.py index 24712bb..789744d 100644 --- a/tests/e2e/test_extract_entities.py +++ b/tests/e2e/test_extract_entities.py @@ -18,6 +18,6 @@ async def test_blocking(self): # The blocking lifecycle end to end: one `execute` call, then narrow. # Extraction finishes well under the hosted ~30s cap, so the blocking mode owns this demo. bundle = BUNDLE_PATH.read_text() - main_stuff = await execute_pipe(pipe_code="extract_entities", bundle=bundle, inputs={"text": SAMPLE_TEXT}) + main_stuff = await execute_pipe(pipe_code="extract_entities", mthds_contents=[bundle], inputs={"text": SAMPLE_TEXT}) entities = ExtractedEntities.model_validate(main_stuff) assert any("Curie" in person for person in entities.people) diff --git a/tests/e2e/test_generate_image.py b/tests/e2e/test_generate_image.py index 74489ff..c04b3b4 100644 --- a/tests/e2e/test_generate_image.py +++ b/tests/e2e/test_generate_image.py @@ -17,7 +17,7 @@ async def test_detached(self): # start, get an id back, then pick the run up again through that id alone. # Image generation outlives the ~30s blocking cap, so it is the demo detached mode owns. bundle = BUNDLE_PATH.read_text() - run_id = await start_pipe(pipe_code="generate_image", bundle=bundle, inputs={"image_prompt": SAMPLE_PROMPT}) + run_id = await start_pipe(pipe_code="generate_image", mthds_contents=[bundle], inputs={"image_prompt": SAMPLE_PROMPT}) assert run_id main_stuff = await attend_run(run_id) image = Image.model_validate(main_stuff) diff --git a/tests/e2e/test_summarize_pdf.py b/tests/e2e/test_summarize_pdf.py index 0c82efb..599e265 100644 --- a/tests/e2e/test_summarize_pdf.py +++ b/tests/e2e/test_summarize_pdf.py @@ -19,7 +19,7 @@ async def test_attended(self): # The attended lifecycle end to end: encode the PDF, start a durable run, poll it, narrow. bundle = BUNDLE_PATH.read_text() inputs = {"document": build_document_input(SAMPLE_PDF)} - main_stuff = await start_and_wait(pipe_code="summarize_pdf", bundle=bundle, inputs=inputs) + main_stuff = await start_and_wait(pipe_code="summarize_pdf", mthds_contents=[bundle], inputs=inputs) summary = DocumentSummary.model_validate(main_stuff) assert summary.title assert summary.doc_type diff --git a/tests/unit/test_bootstrap_script.py b/tests/unit/test_bootstrap_script.py index 3f9cd5c..75d5551 100644 --- a/tests/unit/test_bootstrap_script.py +++ b/tests/unit/test_bootstrap_script.py @@ -47,7 +47,7 @@ def write_template(root: Path, *, extra_pyproject: str = "") -> None: ] [tool.setuptools.package-data] -piper = ["py.typed", "methods/*/main.mthds"] +piper = ["py.typed", "methods/*/*.mthds"] "piper.generated.extract_entities" = ["codegen.lock"] [tool.ruff]