Skip to main content
A model is one Python class that extends ReactorApp. It declares what a client can change and what a client receives, loads its weights once, and produces the next output whenever the runtime asks. This page takes a working model apart so you can see where each of those lives and build your own the same way. The example is the starter from Test locally, trimmed to the parts that matter.
Porting a model written against an earlier runtime? ReactorModel is the previous name for this class. It still imports, with a deprecation warning, and everything on this page applies to it.

The ReactorApp class

starter.py
The file defines three classes. StarterState is what the client controls. Think of it as a set of knobs. When a client turns one, whether that is a web app, a script, or a robot, the value changes inside your model. StarterOutput is what the model sends back. It lists the tracks your model sends to clients. Here there is one, a video track called main_video. Starter is the model. It has two methods the runtime calls for you:

load() runs once, when the container starts

Load your weights here.

generate() runs once per step, while a client is connected

It receives the current input values and returns a StarterOutput. Here each call produces one video frame.
That is all it takes to define a model that reads client inputs and generates output in real time. fps sets the frame rate, and @session_ended resets the model when a session ends. Sessions & Clients covers hooks like that one.

Processing inputs and outputs

Client inputs often need converting into the form the model expects. In the example below, the state holds an action name and two mouse coordinates, and the engine wants them as one WorldInput. In the other direction, the model returns a raw array, not an Output. Two optional hooks around generate() cover this: process_input() prepares the input before generation, and process_output() turns the result into what the client receives.
A diamond-shaped loop: process_input at the top, generate on the right, process_output at the bottom, and the application on the left, with arrows running clockwise. A second arrow leaves process_output downward, labeled output to clients.

One frame, clockwise: prepare the input, generate, shape the output, then back through the application, where client input has landed in the state.

process_input() runs before generate(). It reads the state and decides what the model gets. It can also decide that this is not the moment to call the model at all, for example while the client has paused, or before a required upload has arrived. Raising ApplicationError skips the frame, and the runtime asks again a moment later. process_output() runs after generate(). It receives what generate() returned and turns it into the Output to send, which is also where a status message to the client would be sent.
Both hooks have defaults. Without them, generate() receives the whole state and its return value must already be an Output, which is what the starter relies on. With them, generate() receives exactly the input you built, and it can stay a thin call into whatever runs your weights. This separation is optional and not enforced. It pays off as the model grows past a few lines: input preparation, generation, and output handling each live in one place, so each is easier to read and change. The Step Loop covers the three calls in detail, and Application and Model covers how far to take the separation.

The manifest

reactor.yaml sits in the folder beside your code and tells the runtime which class to load. Three of its fields describe the model:
reactor.yaml
runtime.import is the Python import path to your class, in module:ClassName form. runtime.config is the file whose path load() receives; leave it out and load() gets None. model.name and model.version identify the release when you deploy. The scaffolded file has more than this, including the build settings and the deployment plan, and the reactor.yaml reference documents every key.

Next

The Step Loop

How the runtime drives the three calls, when to skip a step, and how playout is paced.

Managing State

Declare what a client can set, and the validation each field carries.