Every real-time model needs a way for clients to change inputs mid-generation. A prompt, a style setting, a slider value. In Reactor, you declare these as fields on an InputState dataclass, and the runtime does the rest.
Each public field becomes a client-facing parameter. The runtime auto-generates a set_<field> command for each one. From a client application using the official Reactor Client SDK:
await reactor.sendCommand("set_prompt", { prompt: "a dark forest" });await reactor.sendCommand("set_action", { action: "jump" });await reactor.sendCommand("set_brightness", { brightness: 0.5 });
InputField defines constraints that the runtime enforces automatically. Invalid values from clients are silently rejected and the field keeps its current value.
# Default value (any type)score: float = InputField(default=1.0)# Numeric range (min/max inclusive)brightness: float = InputField(default=1.0, ge=0.0, le=1.0)# String / sequence lengthprompt: str = InputField(default="hello", min_length=1, max_length=500)# Exhaustive allowed valuesstyle: str = InputField(default="none", choices=["none", "oil_paint", "sketch"])# Description (shown in schema)seed: int = InputField(default=42, description="Random seed for generation")
A fresh InputState instance (with all defaults) is created when a client connects. It’s destroyed when they disconnect. No state bleeds between sessions.
Client connects: self.state is a fresh instance with all defaults.
During session: fields are mutated by client commands.
If you need custom logic when a field changes (e.g. encoding a prompt), define an @event handler with the same name. It replaces the auto-generated one:
Prefix a field with _ to hide it from clients. No event is generated, and it doesn’t appear in the schema. Use them to store derived values that should be cleaned up automatically when the session ends.
@dataclassclass GameState(InputState): prompt: str = InputField(default="a sunny meadow") # ✅ Private — hidden from clients, no set_ event generated _embedding: Any = None
The _embedding from the override example above is a good use case: the event handler computes it, inference() reads it, and the runtime destroys the entire state on disconnect — no manual cleanup needed.