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

# API reference

> The PolicyClient class and the environment variables it reads.

```python theme={null}
from relay import PolicyClient
```

## PolicyClient

```python theme={null}
PolicyClient(model: str)
```

Opens a WebSocket to the server for `model` and reads its metadata. If the server offers a direct route to its GPU container, the client reconnects there and closes the proxied connection.

<ParamField path="model" type="str" required>
  The model ID: `"pi05"`, `"pi0"` or `"groot"`. It fills `{model}` in `WALLE_URL`.
</ParamField>

The constructor blocks until the server answers, with no timeout. On a server that is still loading its model, that can take a few minutes.

**Raises**

* `KeyError` if `WALLE_URL` is not set.
* `websockets.exceptions.InvalidStatus`, `InvalidURI` or `OSError` if the server can't be reached.

***

## infer

```python theme={null}
policy.infer(obs: dict) -> np.ndarray | dict
```

Sends one observation and waits for the policy's actions.

<ParamField path="obs" type="dict" required>
  Camera frames, state, prompt and any [request options](/observations#request-options). The keys depend on the model. See [Observations](/observations).
</ParamField>

<ResponseField name="actions" type="np.ndarray | dict[str, np.ndarray]">
  π0 and π0.5 return a `float32` array of shape `(action_horizon, action_dim)`, for example `(50, 32)`. GR00T returns a dict of `float32` arrays, one per action group, each of shape `(1, action_horizon, k)`.
</ResponseField>

<ResponseField name="error" type="dict">
  If the server rejects the observation, `infer` returns `{"type": "error", "message": str}` and does **not** raise. The connection stays open, so you can fix the observation and call again. Check for this before executing actions. See [Troubleshooting](/troubleshooting).
</ResponseField>

The returned arrays are read-only views of the received bytes. Call `.copy()` on one if you need to modify it in place.

***

## reset

```python theme={null}
policy.reset() -> None
```

Starts the next observation fresh, as a new episode. It clears every session on this connection. Call it between episodes. See [Episodes and reset](/control-loop#episodes-and-reset).

***

## get\_server\_metadata

```python theme={null}
policy.get_server_metadata() -> dict
```

Returns the settings the server sent on connect. It makes no network call.

<ResponseField name="action_horizon" type="int">
  Actions per chunk.
</ResponseField>

<ResponseField name="image_resolution" type="list[int]">
  `[height, width]` the server resizes frames to.
</ResponseField>

<ResponseField name="needs_session_id" type="bool">
  Whether observations should carry a `session_id`.
</ResponseField>

<ResponseField name="action_dim" type="int">
  π0 and π0.5 only. Width of each action.
</ResponseField>

<ResponseField name="max_cameras" type="int">
  π0 and π0.5 only. Maximum cameras per observation.
</ResponseField>

<ResponseField name="action_space" type="str">
  π0 and π0.5 only. `"joint_position"`.
</ResponseField>

<ResponseField name="action_keys" type="list[str]">
  GR00T only. Keys of the returned action dict.
</ResponseField>

<ResponseField name="embodiment_tag" type="str">
  GR00T only. The robot embodiment the server runs.
</ResponseField>

<ResponseField name="direct_url" type="str">
  Present when the server offers a direct route. The client uses it automatically.
</ResponseField>

***

## close

```python theme={null}
policy.close() -> None
```

Closes the connection. Any call after `close()` raises `websockets.exceptions.ConnectionClosed`.

## Environment variables

`relay` reads these from the environment, or from a `.env` file in the working directory or any parent directory. Variables already set in the environment take precedence.

| Variable | Default | Meaning |
| - | - | - |
| `WALLE_URL` | required | Server URL template with a `{model}` placeholder, for example `https://<workspace>--walle-{model}.modal.run` |
| `WALLE_DIRECT` | `1` | Set to `0` to stay on Modal's web proxy instead of switching to the direct route |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.