Core interfaces#
When you wire a new robot or primitive into RPent, you implement the interfaces below. Walkthroughs: Add a New Robot, Add an Action Primitive. Repo layout: System Internals.
Robot entry#
After you add robots/<robot>/, the package __init__.py re-exports two
functions implemented in robot_spec.py for main.py to call:
def get_robot_spec() -> RobotSpec: ...
def get_toolkit(
*,
primitives_kwargs,
dashboard_events: DashboardEventSink,
config: RunConfig,
): ...
get_robot_spec returns a RobotSpec. You supply:
Field / hook |
What you provide |
|---|---|
|
Robot name for |
|
A |
|
Optional Dashboard description. |
|
Register this robot’s CLI flags (e.g. |
|
Validate args and return |
|
Start or attach to all runtime components, or to the component names in
the optional selection, and build |
get_toolkit usually passes primitives_kwargs into your robot subclass;
dashboard_events and config are supplied by the active runner. It must
construct a MemoryManager (rooted at the configured
config.prompt_vars["memory_dir"], falling back to
get_memory_dir(robot_name) when unset) and pass it to the toolkit.
Memory access permissions are configured on the manager. Robots that need
extra toolkit arguments may declare them as keyword-only parameters; LIBERO
additionally uses mode, attempts_per_session, and state_output_dir.
Reference: robots/libero/robot_spec.py.
Planner#
Most users pick a built-in api, claude_code, or codex planner — see
Agentic Planner. Only custom planners need
rpent.planner.base.Planner:
def solve(
self,
*,
system_prompt: str,
user_message: str,
toolkit: Toolkit,
max_turns: int,
input_queue=None,
dashboard_interaction=None,
) -> PlannerResult: ...
Contract: pass toolkit.get_tools_spec() to the model; dispatch each call via
toolkit.execute_tool(name, input_dict); feed results back to the model; return
PlannerResult on the finish tool or when turns are exhausted.
Toolkit#
Subclass Toolkit in robots/<robot>/toolkit.py and register robot tools with
add_tool:
def add_tool(self, name: str, spec: dict, handler) -> None: ...
Argument |
Meaning |
|---|---|
|
Tool name the LLM sees. |
|
Tool description and parameter schema ( |
|
Implementation; must return a ``dict``. Set |
The base class already registers common file tools; call super().__init__() then
add_tool for robot tools. Per-step state and view_env_state are in
Add an Action Primitive.
Inter-process communication#
Relevant when attaching to existing servers or writing env_server / vla_server.
Client endpoints — expose in add_cli_args and parse in the applicable
normal-CLI or Dashboard runtime hook:
[protocol://]host:port # defaults to http when protocol is omitted
Common flags: --env-endpoint, --vla-endpoint. The default http sends
JSON over POST /call, encoding NumPy arrays as
{"__ndarray__": <base64>, "dtype": ..., "shape": ...} and NumPy scalars as
{"__npscalar__": <value>, "dtype": ...} so dtypes survive the round trip;
switch to socket
for large or history-stacked nested-NumPy observations to move length-prefixed
pickle frames and skip repeated JSON encoding. Pickle is unsafe on untrusted
input, so only point socket at trusted endpoints.
Environment and VLA clients should normally subclass BaseEnvClient and
BaseVLAClient; their servers should subclass BaseEnvFacade and
BaseVLAFacade and register extension routes through _register_rpc. The
bases provide common routing and locking on top of RpcFacade. Subclass
RpcFacade directly only for a service type without a specialized base. Do
not implement healthz or shutdown in application subclasses.
Details are in the env_server / vla_server sections of Add a New Robot.