ReactorApp with the weights loaded in load() and the
inference written straight into generate() is a complete model, and for a small one that is
the right shape. This page is about what to do when the model grows: the pattern the runtime’s own
examples follow, and the reasons behind it.
The idea is a split into two halves. The model is a black box: give it an input, it produces a
result. The application is everything around it that a client can see or touch.
The application owns everything a client can observe. The model owns the weights and produces a result from an input.
generate() in a loop. The application half can be read without
opening the model, since everything a client can observe is in one place. And because the two
share nothing but two dataclasses, either can change without touching the other.
The examples are the runtime’s Waypoint: waypoint_model.py is the model half, waypoint.py is
the application half.
The model half
A plain Python class with three methods. It imports nothing fromreactor_runtime, knows nothing
about clients, tracks, or commands, and runs in a notebook with Reactor uninstalled.
waypoint_model.py
The application half
TheReactorApp constructs the model in load(), holds it under an attribute, and its
generate() is one line that forwards to it. Everything else on the class is about the client.
waypoint.py
Where the halves meet
The two halves talk through two dataclasses you define: the inputprocess_input() builds and
the result generate() returns. The Step Loop covers
how they travel.
The only things that cross between the halves are the two dataclasses.
reset(), and since handlers run between steps that call is
safe.
Coming back, everything the application needs to know rides in the result. Waypoint reads
result.seed_id to know which seed the world holds and result.index to tag frames, and never
touches self.engine.index. Design the result as the model’s public face.
Refusing is the application’s, failing is the model’s
process_input() refuses a step with ApplicationError for a fact about the client: paused, no
prompt yet, waiting for frames. The model is not called and the loop asks again shortly.
generate() fails a step by raising the model’s own exception for a fact about the model: it
cannot continue from the state it holds. The runtime delivers it to process_output() as
outcome.error, and the application decides whether to recover or let the session end.
Never raise ApplicationError from the model half. It does not import the runtime, and whether to
refuse is not its call.
Next
The Step Loop
How the input and the result travel between the three calls.
Session Recording
Record sessions and let clients capture clips.