Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
4530341
Initial plan
Copilot Jan 28, 2026
d529dee
Add Resource class and integrate with Component, RayProcess, and Tuner
Copilot Jan 28, 2026
f59376c
Add comprehensive tests for Resource implementation
Copilot Jan 28, 2026
0219833
Add examples for resource requirements in Ray processes
Copilot Jan 28, 2026
3ec7854
Fix type annotations and address code review feedback
Copilot Jan 28, 2026
9515154
Address code review feedback: generalize suffixes, make memory intege…
Copilot Jan 31, 2026
c400a97
Move suffixes to top
toby-coleman Feb 3, 2026
a38aa88
Rework logic for tune placement bundles
toby-coleman Feb 3, 2026
573a0e1
Merge remote-tracking branch 'origin/main' into copilot/implement-res…
toby-coleman Feb 7, 2026
31092ab
Docs config improved for schemas
toby-coleman Feb 7, 2026
6bf7017
Tidy up documentation on resources example
toby-coleman Feb 7, 2026
bae7c46
Add resources for smoke tests
toby-coleman Feb 7, 2026
a9a4ef5
Make sure resources are available in runner
toby-coleman Feb 7, 2026
c14cbaf
Remove non-existent cross refs in docs
toby-coleman Feb 7, 2026
2588544
Fixup types
toby-coleman Feb 7, 2026
d2e7aa2
Test that actor is passed the resource requirements
toby-coleman Feb 7, 2026
3301419
Increase timeouts on test CI
toby-coleman Feb 7, 2026
9b361df
Typing
toby-coleman Feb 7, 2026
d7c87c6
Fixup test
toby-coleman Feb 7, 2026
a3c9555
Increase tuner test timeout
toby-coleman Feb 7, 2026
c9aa1ce
Correct format for placement groups in Ray tune
toby-coleman Feb 7, 2026
155c571
Typo
toby-coleman Feb 8, 2026
a562b88
Coverage
toby-coleman Feb 8, 2026
5fb86bc
refactor: Resource value validation
chrisk314 Feb 11, 2026
b0fd414
fix: Inconsistent type annotation syntax
chrisk314 Feb 11, 2026
3f8031f
Allows declaring resources at Component declaration time
chrisk314 Feb 11, 2026
82a76cf
Enhances tests
chrisk314 Feb 11, 2026
2892fe9
Ignore lint error
chrisk314 Feb 11, 2026
ea310c1
Update docs and examples to show class-level resource declaration
Copilot Feb 11, 2026
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
6 changes: 3 additions & 3 deletions .github/workflows/lint-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ jobs:
test-unit:
name: Tests - unit
runs-on: ubuntu-latest
timeout-minutes: 5
timeout-minutes: 8
strategy:
matrix:
python_version: [3.12, 3.13]
Expand Down Expand Up @@ -113,7 +113,7 @@ jobs:
test-integration:
name: Tests - integration
runs-on: ubuntu-latest
timeout-minutes: 6
timeout-minutes: 10
strategy:
matrix:
python_version: [3.12, 3.13]
Expand Down Expand Up @@ -157,7 +157,7 @@ jobs:
test-integration-tuner:
name: Tests - integration:tuner
runs-on: ubuntu-latest
timeout-minutes: 5
timeout-minutes: 8
strategy:
matrix:
python_version: [3.12, 3.13]
Expand Down
2 changes: 1 addition & 1 deletion docs/api/schemas/schemas.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
::: plugboard.schemas
::: plugboard_schemas
65 changes: 65 additions & 0 deletions docs/examples/tutorials/running-in-parallel.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,68 @@ Specifying the process type and channel builder type in the YAML is the only cha

1. Tell Plugboard to use a [`RayProcess`][plugboard.process.RayProcess] instead of the default [`LocalProcess`][plugboard.process.LocalProcess].
2. Also change the connector builder to [`RayConnector`][plugboard.connector.RayConnector], which will build [`RayChannel`][plugboard.connector.RayChannel] objects when creating the `Process`.

## Specifying resource requirements

When running components on Ray, you can specify resource requirements for each component to control how Ray allocates computational resources. This is particularly useful when you have components with different resource needs (e.g., CPU-intensive vs GPU-intensive tasks) and you are running on a Ray cluster.

!!! tip
Normally Ray will start automatically when you are using Plugboard locally. If you want to start a separate Ray instance, for example so that you can control the configuration options, you can launch it from the [CLI](https://docs.ray.io/en/latest/ray-core/starting-ray.html). For example, this command will start a Ray instance with enough resources to run the example below.

```sh
uv run ray start --head --num-cpus=4 --num-gpus=1 --resources='{"custom_hardware": 5}'
```

### Declaring resources at component definition

The recommended way to specify resource requirements is to declare them as a class attribute when defining your component. This makes the resource requirements explicit and part of the component's definition:

```python
from plugboard.component import Component
from plugboard.schemas import Resource

class CPUIntensiveTask(Component):
"""Component that requires more CPU resources."""

io = IO(inputs=["x"], outputs=["y"])
resources = Resource(cpu=2.0) # Declare resources at class level

async def step(self) -> None:
# Your component logic here
pass
```

### Overriding resources at instantiation

You can also override resource requirements when creating component instances. This is useful when you want to use the same component class with different resource requirements:

```python
# Override the class-level resource requirements
component = CPUIntensiveTask(
name="my-task",
resources=Resource(cpu=4.0) # Override to use 4 CPUs instead of 2
)
```

### Example

For example, you can specify [`Resource`][plugboard.schemas.Resource] requirements like this when defining components:

```python
--8<-- "examples/tutorials/004_using_ray/resources_example.py:resources"
```

Or override them in YAML configuration:

```yaml
--8<-- "examples/tutorials/004_using_ray/resources-example.yaml:10:"
```

1. Override DataProducer to require 1 CPU (instead of the default 0.001).
2. CPUIntensiveTask already declares cpu: 2.0 at the class level, so this matches the class definition.
3. Add 512MB memory requirement to CPUIntensiveTask (extending the class-level resources).
4. Requires 0.5 CPU, specified in Kubernetes-style format (500 milli CPUs). This matches the class-level declaration.
5. Requires 1 GPU, matching the class-level declaration.
6. Add a custom resource called `custom_hardware`. This needs to be specified in the configuration of your Ray cluster to make it available.

See the [Ray documentation](https://docs.ray.io/en/latest/ray-core/scheduling/resources.html) for more information about specifying resource requirements.
47 changes: 47 additions & 0 deletions examples/tutorials/004_using_ray/resources-example.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Example YAML configuration with resource requirements
#
# This configuration demonstrates how to specify resource requirements
# for components in a Plugboard process. Resources can be specified as:
# - Numerical values: cpu: 2.0
# - Milli-units: cpu: "250m" (equals 0.25)
# - Memory units: memory: "100Mi" (equals 100 * 1024 * 1024 bytes)
#
# Note: Resources can be declared at the component class level (recommended)
# or overridden in the YAML configuration as shown below.
plugboard:
process:
type: plugboard.process.RayProcess
connector_builder:
type: plugboard.connector.RayConnector
args:
name: resource-example-process
components:
- type: examples.tutorials.004_using_ray.resources_example.DataProducer
args:
name: producer
iters: 10
resources:
cpu: 1.0 # (1)!
- type: examples.tutorials.004_using_ray.resources_example.CPUIntensiveTask
args:
name: cpu-task
# CPUIntensiveTask has class-level resources (cpu: 2.0)
# Override to use more memory
resources:
cpu: 2.0 # (2)!
memory: "512Mi" # (3)!
- type: examples.tutorials.004_using_ray.resources_example.GPUTask
args:
name: gpu-task
# GPUTask has class-level resources (cpu: "500m", gpu: 1)
# Can override or extend with custom resources
resources:
cpu: "500m" # (4)!
gpu: 1 # (5)!
resources:
custom_hardware: 2 # (6)!
connectors:
- source: producer.output
target: cpu-task.x
- source: cpu-task.y
target: gpu-task.data
95 changes: 95 additions & 0 deletions examples/tutorials/004_using_ray/resources_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"""Example demonstrating resource requirements for components in RayProcess."""

import asyncio
import typing as _t

import ray

from plugboard.component import Component, IOController as IO
from plugboard.connector import RayConnector
from plugboard.process import RayProcess
from plugboard.schemas import ComponentArgsDict, ConnectorSpec, Resource


class CPUIntensiveTask(Component):
"""Component that requires more CPU resources.

Resource requirements are declared as a class attribute.
"""

io = IO(inputs=["x"], outputs=["y"])
resources = Resource(cpu=2.0) # Declare resources at class level

async def step(self) -> None:
"""Execute CPU-intensive computation."""
# Simulate CPU-intensive work
result = sum(i**2 for i in range(int(self.x * 10000)))
self.y = result


class GPUTask(Component):
"""Component that requires GPU resources.

Resource requirements are declared as a class attribute.
"""

io = IO(inputs=["data"], outputs=["result"])
resources = Resource(cpu="500m", gpu=1) # Declare resources at class level

async def step(self) -> None:
"""Execute GPU computation."""
# Simulate GPU computation
self.result = self.data * 2


class DataProducer(Component):
"""Produces data for processing."""

io = IO(outputs=["output"])

def __init__(self, iters: int, **kwargs: _t.Unpack[ComponentArgsDict]) -> None:
"""Initialize DataProducer with iteration count."""
super().__init__(**kwargs)
self._iters = iters

async def init(self) -> None:
"""Initialize the data sequence."""
self._seq = iter(range(self._iters))

async def step(self) -> None:
"""Produce the next data value."""
try:
self.output = next(self._seq)
except StopIteration:
await self.io.close()


async def main() -> None:
"""Run the process with resource-constrained components."""
# --8<-- [start:resources]
# Resources can be declared at the class level (see CPUIntensiveTask and GPUTask above)
# or overridden when instantiating components
process = RayProcess(
components=[
DataProducer(name="producer", iters=5), # Uses default resources
CPUIntensiveTask(name="cpu-task"), # Uses class-level resources (2.0 CPU)
GPUTask(name="gpu-task"), # Uses class-level resources (0.5 CPU, 1 GPU)
],
connectors=[
RayConnector(spec=ConnectorSpec(source="producer.output", target="cpu-task.x")),
RayConnector(spec=ConnectorSpec(source="cpu-task.y", target="gpu-task.data")),
],
)
# --8<-- [end:resources]

async with process:
await process.run()

print("Process completed successfully!")


if __name__ == "__main__":
if not ray.is_initialized():
# Ray must be initialised with the necessary resources
ray.init(num_cpus=5, num_gpus=1, resources={"custom_hardware": 10}, include_dashboard=True)
asyncio.run(main())
3 changes: 2 additions & 1 deletion mkdocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ plugins:
default_handler: python
handlers:
python:
paths: [src]
paths: [plugboard, plugboard-schemas]
options:
docstring_style: google
show_source: false
Expand Down Expand Up @@ -113,6 +113,7 @@ watch:
- docs
- examples
- plugboard
- plugboard-schemas
- README.md
- CONTRIBUTING.md

Expand Down
3 changes: 2 additions & 1 deletion plugboard-schemas/plugboard_schemas/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from importlib.metadata import version

from ._common import PlugboardBaseModel
from .component import ComponentArgsDict, ComponentArgsSpec, ComponentSpec
from .component import ComponentArgsDict, ComponentArgsSpec, ComponentSpec, Resource
from .config import ConfigSpec, ProcessConfigSpec
from .connector import (
DEFAULT_CONNECTOR_CLS_PATH,
Expand Down Expand Up @@ -77,6 +77,7 @@
"ProcessArgsDict",
"ProcessArgsSpec",
"RAY_STATE_BACKEND_CLS_PATH",
"Resource",
"StateBackendSpec",
"StateBackendArgsDict",
"StateBackendArgsSpec",
Expand Down
Loading
Loading