Setting up a new policy server#
ToyBlocks provides a base class and a WebSocket server so any model can be evaluated with the same pipeline. The model implementation lives in its own repository; ToyBlocks only provides the interface.
1. Subclass BasePolicy in your model’s package:
from toyblocks.serving import BasePolicy
class MyPolicy(BasePolicy):
def __init__(self):
self.model = load_my_model()
def infer(self, obs: dict) -> dict:
action = self.model.predict(obs)
return {"actions": action} # np.ndarray, shape (action_dim,) or (T, action_dim)
The infer method receives a dict of numpy arrays and must return a dict with at
least an "actions" key. Any extra ndarray keys returned alongside it are made
available to the eval loop on PolicyClientAdapter.last_aux (this is how
action_pd_mode is passed through).
2. Serve it using toyblocks.serving.PolicyServer:
from toyblocks.serving import PolicyServer
server = PolicyServer(MyPolicy(), host="0.0.0.0", port=8000)
server.serve_forever()
There is no generic launcher script; wrap these lines in your own entry point.
src/toyblocks/serving/serve_maniflow_policy.py (run as
python -m toyblocks.serving.serve_maniflow_policy) is a complete example: a tyro CLI that
loads the ManiFlow checkpoint into a BasePolicy and serves it with PolicyServer.
Point evaluate.py at the server with --policy_host and
--policy_port. launch_eval.py starts only the ManiFlow server (--policy maniflow or
molmo2_maniflow), so run your own server alongside evaluate.py instead.
3. (Optional) Add an observation transform if your model expects a different
observation format than the ToyBlocks env produces. Subclass BaseObsTransform in
src/toyblocks/serving/obs_transforms/ and register it in _lazy_registry, then use
--obs_transform <name> at eval time. Transforms can also override
action_postprocess to rewrite the policy response before the action chunk broker.