> ## 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.

# Python

> Every class and function in relay, and the environment variables it reads.

```
relay/
├── inference/   Policy, Codec: action chunks from a policy server
├── sft/         SFTConfig, train: fine-tuning jobs
└── training/    ServiceClient: training loops you write
```

## Environment

| Variable | Is |
| - | - |
| `RELAY_API_KEY` | Your API key, from the [dashboard](https://relayintelligence.org/dashboard/api-keys). The training APIs need it. |
| `RELAY_BASE_URL` | The training API's URL, if not the default. |

## relay.inference

Also `relay.Policy` and `relay.Codec`.

### Policy

```python theme={null}
relay.Policy(url: str, codec: Codec = Codec.JPEG_Q90, headers: dict[str, str] | None = None)
```

Connects to a policy server and reads its metadata. See [Inference](https://docs.relayintelligence.org/inference.md).

| Method | Returns |
| - | - |
| `infer(obs: dict) -> dict` | `{"actions", "policy_timing", "server_timing"}` for one observation. `RuntimeError` with the server's traceback if it fails. |
| `get_server_metadata() -> dict` | `model`, `cameras`, `state_dim`, `action_horizon`, `action_dim`, `num_steps` |
| `close()` | Closes the connection. A `with` block closes it too. |

### Codec

`Codec.LOSSLESS`, `Codec.JPEG_Q100`, `JPEG_Q98`, `JPEG_Q95`, `JPEG_Q92`, `JPEG_Q90`, `JPEG_Q85`, `JPEG_Q50`. See [Sending images](https://docs.relayintelligence.org/inference.md#sending-images).

## relay.sft

### SFTConfig

```python theme={null}
SFTConfig(
    model: str,
    dataset_name: str,
    batch_size: int = 128,
    learning_rate: float = 2.5e-5,
    steps: int = 6000,
    finetune: str = "lora",              # or "full"
    lora_rank: int = 32,
    lora_alpha: float | None = None,     # None: lora_rank
    targets: list[str] | None = None,    # relay.sft.TARGETS
    cameras: dict[str, str] | None = None,
    name: str | None = None,
)
```

See [Fine-tuning](https://docs.relayintelligence.org/fine-tuning.md) and [Where training goes](https://docs.relayintelligence.org/targets.md).

### Functions

| Function | Returns |
| - | - |
| `train(config: SFTConfig) -> FineTuneJob` | A started job. |
| `retrieve(job_id: str) -> FineTuneJob` | A job, up to date. |
| `list_jobs() -> list[FineTuneJob]` | Your jobs, newest first. |

`train` is in `relay.sft.train`; all three are in `relay.sft`.

### FineTuneJob

| Method | Does |
| - | - |
| `refresh() -> FineTuneJob` | Brings it up to date. |
| `wait(poll: float = 30.0) -> FineTuneJob` | Waits until it's completed or cancelled. `RuntimeError` if it failed. |
| `cancel() -> FineTuneJob` | Stops it. |
| `events() -> list[dict]` | Its logged steps, then a last `{"event": "finished"}` or `{"event": "cancelled"}`. |

Attributes: `id`, `status`, `created_at`, `step`, `loss`, `error`, `output_dir`, and the config's fields.

## relay.training

### ServiceClient

```python theme={null}
ServiceClient().create_lora_training_client(
    base_model: str, rank: int = 32, targets: list[str] | None = None, alpha: float | None = None,
    action_horizon: int = 10, codec: Codec = Codec.LOSSLESS, timeout: float = 1800,
) -> TrainingClient

ServiceClient().create_full_training_client(
    base_model: str, targets: list[str] | None = None,
    action_horizon: int = 10, codec: Codec = Codec.LOSSLESS, timeout: float = 1800,
) -> TrainingClient
```

Each starts a session and waits until it's ready, or `TimeoutError` after `timeout` seconds.

### TrainingClient

| Method | Returns |
| - | - |
| `forward_backward(data: list[Datum], loss_fn: str = "flow_matching")` | `Future[ForwardBackwardOutput]` |
| `forward_backward_custom(data: list[Datum], loss_fn)` | `Future[ForwardBackwardOutput]`. `loss_fn(data, outputs) -> (loss, metrics)`. |
| `forward(data: list[Datum])` | `Future[dict[str, np.ndarray]]`: `velocity`, `target`, `x_t`, `noise`, `time` |
| `optim_step(adam_params: AdamParams)` | `Future[OptimStepOutput]` |
| `sample(observations: dict \| list[dict], num_steps: int = 10)` | `Future[np.ndarray]`: `(action_horizon, action_dim)`, or a batch of them |
| `save_state(name: str)` | `Future[str]` |
| `load_state(name: str)` | `Future[None]` |
| `save_weights_and_get_sampling_client(name: str)` | `SamplingClient` |
| `close()` | Ends the session. A `with` block ends it too. |

Attributes: `session_id`, `info`.

### SamplingClient

`sample(observations, num_steps=10) -> Future[np.ndarray]` with the weights saved under `name`; `path` is where they are.

### types

| Type | Fields |
| - | - |
| `Datum` | `observation: dict`, `actions: np.ndarray \| None = None`, `weight: float = 1.0` |
| `AdamParams` | `learning_rate`, `beta1=0.9`, `beta2=0.95`, `eps=1e-8`, `weight_decay=0.0`, `grad_clip_norm=1.0` |
| `ForwardBackwardOutput` | `metrics: dict[str, float]`, `loss_fn_outputs: list[dict]` |
| `OptimStepOutput` | `metrics: dict[str, float]`: `grad_norm`, `skipped` |


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