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