> ## Documentation Index
> Fetch the complete documentation index at: https://docs.reactor.inc/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> To build and serve your own model, start at /deploy/development/quickstart and /deploy/development/overview. Deploying is the default path: reactor init scaffolds a workspace, reactor auth login authenticates, and reactor model deploy registers the model, publishes the release with the weights/ folder, and activates it on Reactor's GPUs, in one command from that workspace. Docker must be running, because the publish step builds the image locally. Bump model.version in reactor.yaml before redeploying a change, because a release that already has an image is reactivated as it is. Deployment access is granted per account, so contact team@reactor.inc if a deploy is refused. Every key in reactor.yaml is documented at /deploy/platform/reactor-yaml. Model code imports reactor_runtime; Python client code imports reactor_sdk. The runtime overview explains the model interface. Running the model on your own machine with reactor run is optional and needs a GPU you attach with --gpus; /deploy/development/local-testing covers that loop and pairs a complete brightness model with a Python client test in a separate brightness-test workspace.
> Reactor hosts multiple models, each with its own connect slug (modelName) and command/event schema. The catalog of every model — slug, typed SDK package, and links to its schema — is at /model-api-reference/overview. Some models expose one slug per experience (e.g. HappyOyster); always take the slug from the model's own pages, never guess it.
> Fastest path to a working app: `npx create-reactor-app my-app --model=<slug>` scaffolds a complete app with secure auth wired up. Typed TypeScript SDKs are published as @reactor-models/<model>; Python uses the base reactor-sdk package.
> Auth: exchange an API key (rk_...) for a JWT via POST https://api.reactor.inc/tokens from your server. Never put the API key in client-side code.
> Append .md to any docs URL for clean Markdown. Search these docs via the MCP server at https://docs.reactor.inc/mcp.

# DistributedRunner

> Start model workers in separate processes and drive them with one call.

Start one or more processes, each with its own instance of the same worker class. Every rank runs
that class's `generate(input)` method with the same input. The runner waits for every rank and
returns rank 0's result.

Define your worker by extending [DistributedWorker](/deploy/runtime-reference/distributedworker).
Implement `load()`, `generate()`, and `reset()`, then pass the class to the runner.

<Note>
  Development preview. See [availability](/deploy/development/distributed-workers#availability)
  before selecting a runtime version.
</Note>

```python theme={"theme":{"light":"github-light","dark":"github-dark-high-contrast"}}
from reactor_runtime.distributed import DistributedRunner

runner = DistributedRunner(
    MyWorker,
    world_size=2,
    load_kwargs={"weights_root": weights_root},
)
```

See [Multi-GPU inference](/deploy/development/distributed-workers) for the `ReactorApp` example and
complete LingBot example.

## Constructor

| Argument             | Default  | Meaning                                                                                                        |
| -------------------- | -------- | -------------------------------------------------------------------------------------------------------------- |
| `worker_cls`         | Required | An importable class with a no-argument constructor and `load`, `generate`, and `reset` methods.                |
| `world_size`         | `1`      | Positive integer count of local workers. Explicitly chosen by the caller.                                      |
| `load_kwargs`        | `None`   | Picklable keyword arguments passed to each worker's `load()`.                                                  |
| `call_timeout`       | `30.0`   | Seconds to wait for every answer to `generate()` or `reset()`.                                                 |
| `start_timeout`      | `3600.0` | Seconds to wait for all workers to finish startup and load.                                                    |
| `init_process_group` | `True`   | Initialize a PyTorch process group when `world_size > 1`. Disable only for protocol tests that do not need it. |

With CUDA, each rank binds to its visible device ordinal and the process group uses NCCL. The CPU
path uses Gloo. A single worker still runs in a separate process but skips process-group
initialization. The runner uses the `spawn` multiprocessing context.

## Methods

<ResponseField name="start()" type="None">
  Construct each worker, assign its rank, world size, and device, then call `load(**load_kwargs)`.
  Block until all workers load. A runner starts once. On startup failure, the runner shuts down its
  workers and raises the error.
</ResponseField>

<ResponseField name="generate(input)" type="Any">
  Send the same input to every worker. Block until every worker answers, then return rank 0's result
  if all succeeded. Other ranks' result values do not return to the caller.
</ResponseField>

<ResponseField name="reset()" type="None">
  Call every worker's `reset()` and wait for every answer. The worker decides which state to
  release. Keeping loaded weights is the convention.
</ResponseField>

<ResponseField name="shutdown()" type="None">
  End the workers and release the runner's shared-memory blocks. Repeated calls are safe. The runner
  also registers this method with `atexit`. Explicit cleanup is useful in scripts.
</ResponseField>

## Properties

<ResponseField name="world_size" type="int">
  The configured number of workers.
</ResponseField>

<ResponseField name="healthy" type="bool">
  Whether the runner has started, remains usable, and has not shut down. This is not an idle
  watchdog. Worker liveness is checked while waiting for a call.
</ResponseField>

## Call contract

When a runner error escapes `ReactorApp.generate()`, the default step loop passes it to
`process_output()` as `outcome.error`. See
[Handle a failed step](/deploy/development/distributed-workers#handle-a-failed-step) for application
recovery. Direct runner calls raise to their caller.

Use one calling thread and one outstanding call. Concurrent calls are unsupported. Inputs and
results must be picklable. Use contiguous CPU NumPy arrays for large payloads to take advantage of
shared memory. The receiver copies the arrays out before the next call.

If all ranks raise the same exception type, the caller receives rank 0's exception and the runner
stays healthy. This does not prove the model's state is recoverable. An exception that cannot cross
the process boundary becomes a `RuntimeError` containing its type and message.

Mixed success, or different exception types, raises
[RankDesync](/deploy/runtime-reference/rankdesync). A dead worker raises
[WorkerCrashed](/deploy/runtime-reference/workercrashed). An unanswered call can raise
[WorkerTimeout](/deploy/runtime-reference/workertimeout). These failures leave the runner unusable.
Shut it down and construct a new one.

Shared-memory allocation can raise
[SharedSlotAllocationFailed](/deploy/runtime-reference/sharedslotallocationfailed).
