hylo-serve¶
Hylo serving: policy server (Hylo + openpi protocols), workcell runtime next to a robot (async chunk execution, hold, watchdog, operator events)
Repository hylo-serve, packages/hylo-serve. Licence: Apache-2.0. Depends on: hylo-core, websockets>=13, fastapi>=0.110, uvicorn>=0.29, pillow>=10.
Registered entry points
| Group | Name | Object |
|---|---|---|
| commands | serve |
hylo.serve.cli:main |
| Module | Summary |
|---|---|
hylo.serve |
|
hylo.serve.cli |
hylo serve policy |
hylo.serve.policy |
Policy server (S1): any Hylo agent as a network service, for robots and for other harnesses |
hylo.serve.runtime |
Real-time episodes next to a robot: the core kernel in realtime mode (core.kernel.Timing) |
hylo.serve.workcell |
Workcell service (S2): runs next to the robot (or simulator) and executes episodes with a remote policy |
hylo.serve¶
hylo.serve.cli¶
hylo serve policy | workcell | env | agent: start a serving role (policy.py, workcell.py) or one component.
Registered as the serve subcommand of hylo (core.cli). env / agent serve a single plugin over WebSocket
(core.remote.ws), e.g. a simulator in its own venv.
hylo.serve.policy¶
Policy server (S1): any Hylo agent as a network service, for robots and for other harnesses.
One WebSocket server, two protocols, chosen by path:
- /hylo the Hylo agent protocol (hylo.agentserver/v0.1, JSON): spec / reset / act / on_step /
on_episode_end. reset carries the client's EnvSpec and the session is refused there, before any
motion, if the agent is not compatible with it (core.compat). Client: core.remote.ws.WSAgent.
- /openpi openpi's protocol (msgpack-numpy): the server sends its metadata, then answers every observation dict
with {"actions": (H, A), "server_timing": {...}}, so openpi clients (DROID, RoboArena, openpi's own
examples) can run our agents. openpi has no reset message: the agent is reset on connect, when the
prompt changes, or when the client sends {"reset": true}.
- /healthz liveness, /meta the served agent's spec as JSON.
One session at a time (a robot talks to one policy; a second client is refused with code 1013, like LeRobot's
admission). The agent is built once and reused across sessions. With campaign, the server follows that
campaign's active version through its active.json (core.deployment): between sessions, never during one, it
rebuilds the agent when a promotion or rollback changed it.
hylo serve policy --agent '{"type": "lerobot.steered", ...}' # or --config EXP.yaml / --campaign DIR
OPENPI_PRESETS
module-attribute
¶
OPENPI_PRESETS: dict[str, dict[str, str]] = {'libero': {'observation/image': 'camera:agentview', 'observation/wrist_image': 'camera:wrist', 'observation/state': 'state:eef_pos=3,eef_axis_angle=3,gripper_qpos=2', 'prompt': 'instruction'}}
PolicyServer ¶
PolicyServer(*, agent: dict[str, Any] | None = None, config: str | None = None, campaign: str | None = None, host: str = '127.0.0.1', port: int = 8000, token: str | None = None, openpi_inputs: dict[str, str] | str = 'libero', openpi_max_steps: int = 10000, log: Callable[[str], None] = lambda m: print(m, file=sys.stderr, flush=True))
openpi_inputs
instance-attribute
¶
openpi_inputs = OPENPI_PRESETS[openpi_inputs] if isinstance(openpi_inputs, str) else dict(openpi_inputs)
observation_from_openpi ¶
hylo.serve.runtime¶
Real-time episodes next to a robot: the core kernel in realtime mode (core.kernel.Timing).
The loop itself (aligned chunk merging, hold action, watchdog, operator channel, virtual / wall clock) lives in
core.kernel, so a benchmark can measure exactly what a workcell runs (timing: {mode: realtime, clock: virtual}
in an experiment config). This module keeps the workcell's configuration (runtime: in a workcell config) and
the result shape the workcell reports.
OperatorChannel ¶
Thread-safe operator input: stop, estop (latched), clear_fault, reset_done, verdict.
RealtimeResult
dataclass
¶
RealtimeResult(outcome: Any, holds: int, chunks: int, safety_interventions: int, verdict: dict[str, Any] | None = None, events: int = 0, extra: dict[str, Any] = dict())
RealtimeEpisodeRunner ¶
RealtimeEpisodeRunner(env: Any, agent: Any, *, config: RealtimeConfig | None = None, safety: Mapping[str, Any] | None = None, trace: TraceStore | None = None, bus: EventBus | None = None, operator: OperatorChannel | None = None)
runner
instance-attribute
¶
runner = EpisodeRunner(env, agent, trace=trace, bus=bus, safety=safety, timing=self.cfg.timing(), operator=self.operator, image_every=self.cfg.image_every, record_privileged=self.cfg.record_privileged)
hold_action ¶
What to execute when no action is queued: the env's own hold_action(last), the last target for absolute
control, or zero motion (gripper kept) for delta / velocity control.
hylo.serve.workcell¶
Workcell service (S2): runs next to the robot (or simulator) and executes episodes with a remote policy.
It owns the environment, the SafetyShield, the real-time runner and the trace, so safety never depends on the
network: if the policy server stalls the robot holds and the watchdog aborts; an e-stop is latched here until an
operator clears it. The policy is reached over the network (policy.protocol: "hylo" = the Hylo agent protocol,
"openpi" = openpi's). Episodes are recorded as an ordinary run (split deployment by default, which no learner
reads unless asked) so replay, judges and the catalog work on them.
hylo serve workcell --config workcell.yaml --port 8100
name: sim-libero
env: {type: libero, remote: {...}} # any env plugin: a simulator or a robot adapter
benchmark: {type: libero, params: {suite: libero_10}} # optional: named tasks and init states
policy: {url: "ws://gpu:8000/hylo", protocol: hylo}
runtime: {clock: wall, refill_steps: 10, max_hold_steps: 40, require_reset_confirm: true, verdict_timeout_s: 60}
safety: {max_delta: 0.5}
HTTP API: hylo.workcell/v0.1 (core.remote.workcell): GET /healthz, GET /status, POST /episodes, POST /operator, WS /events (live stream).