Studio HTTP API#
Everything the Studio does goes through a JSON API on the same port, so the pipeline can be scripted from CI, a notebook or another machine.
Note
The API serves the Studio’s own UI. It is stable enough for scripts but not versioned — endpoints may change between releases.
Conventions#
Base URL |
|
Auth |
None by default. With |
Requests |
JSON bodies ( |
Responses |
JSON. Actions return |
Errors |
|
Example: the whole pipeline#
S=http://127.0.0.1:8765
J='content-type: application/json'
# data: import a folder of clips and convert them
curl -s -X POST $S/api/data/import -H "$J" \
-d '{"source": "/path/to/csvs", "name": "my_clips", "link": true}'
curl -s -X POST $S/api/data/convert -H "$J" \
-d '{"dataset": "my_clips", "which": "missing", "batch_size": 8}'
# preset: Wbc-G1 + the se module, twice the joint-velocity noise
curl -s -X PUT $S/api/presets/se_noisy -H "$J" -d '{
"base": "Wbc-G1", "modules": ["se"],
"overrides": {"observations": {"actor": {"terms": {"joint_vel": {"noise_scale": 2}}}}}}'
# train it and follow the job
curl -s -X POST $S/api/train/launch -H "$J" -d '{
"task": "Wbc-G1--se_noisy", "dataset": "my_clips", "num_envs": 4096, "max_iterations": 30000}'
curl -s $S/api/jobs # status, progress, ETA
curl -s "$S/api/jobs/<job>?tail=40" # one job + log tail
# play the run (watch it in the browser)
curl -s -X POST $S/api/play/configure -H "$J" -d '{
"run": "wbc_g1--se_noisy/<run>", "checkpoint": "model_29999.pt",
"motion": "<library path from /api/play/libraries>", "num_envs": 16}'
The same from Python:
import requests
S = "http://127.0.0.1:8765"
job = requests.post(f"{S}/api/train/launch", json={
"task": "Wbc-G1", "dataset": "samples", "num_envs": 4096, "max_iterations": 30000,
}).json()["job"]
print(requests.get(f"{S}/api/jobs/{job}").json()["progress"])
Robots#
Endpoint |
Description |
|---|---|
|
Registered robots with model summary, datasets and runs |
|
Selected robot: joints, wiring (anchor, keybodies, feet), tasks |
|
Select a robot (background task) |
|
Pose sliders, default / zero pose, show the robot, wiring markers |
Adding a robot (Add a robot from model files):
Endpoint |
Description |
|---|---|
|
Robots folder, saved robots, unfinished uploads |
|
Upload one model file at its relative path (meshes keep their layout) |
|
Copy a folder on the Studio machine |
|
Model files in the folder, the likely main model first |
|
Prepare and inspect the model: bodies, joints, suggested wiring and gains |
|
Show the draft with wiring markers |
|
Settle check: |
|
Save and register ( |
|
A Studio robot’s |
Data#
Endpoint |
Description |
|---|---|
|
Datasets of the robot with source / converted / missing counts |
|
Clip listing; |
|
Joint list · preview state ( |
|
Preview a clip ( |
|
Preview transport and display options |
|
Duration scan (hours, length histogram) |
|
Conversion job ( |
|
New empty dataset · import a folder (copy, or link without copying) |
|
Upload one clip · refresh after an upload batch |
Presets and modules#
Endpoint |
Description |
|---|---|
|
Tasks, your presets, modules, the config directory |
|
Every editable knob of a task (+ modules) with defaults, and the observation term library |
|
Read · write |
|
Read · write |
Training and jobs#
Endpoint |
Description |
|---|---|
|
Tasks of the robot (built-in and presets) · runs on disk |
|
Launch a detached training job → |
|
Jobs with status, progress and ETA · one job with its log tail |
|
Stop · forget a job (its run stays) |
|
Training curves ( |
|
Start TensorBoard if needed and redirect to it |
Play#
Endpoint |
Description |
|---|---|
|
Playback state · runs · motion libraries for a run · clips of the session |
|
Load a policy ( |
|
Stop · pause / resume · single step · reset every env |
|
Pin a clip to an env (it loops) · focus an env |
|
Ghost mode and targets · push the robot · live reward weights |
|
Terrain tiles under the robots (binary mesh, see below) |
Plots#
The data behind the Plots window (Plots). Each channel is
{id, name, group, unit, lower, upper} (unit: rad, m, Nm, reward,
raw or "" for actions; joint torques with effort limits add limit_note, where the
limits come from); ref is NaN where a channel has no reference.
Endpoint |
Description |
|---|---|
|
The clip open in Data, JSON: |
|
A Play env’s record (default |
import json, struct, numpy as np, requests
S = "http://127.0.0.1:8765"
blob = requests.get(f"{S}/api/plots/play", params={"env": 0}).content
(hlen,) = struct.unpack_from("<I", blob)
head = json.loads(blob[4 : 4 + hlen])
rows = head["end"] - head["start"]
flat = np.frombuffer(blob, "<f4", offset=4 + hlen)
robot, ref, meta = np.split(flat, np.cumsum([rows * c for c in head["cols"]])[:2])
robot, ref = robot.reshape(rows, -1), ref.reshape(rows, -1)
knee = [c["id"] for c in head["channels"]].index("left_knee_joint")
ref_knee = ref[:, head["ref_cols"].index(knee)]
rmse = np.sqrt(np.nanmean((robot[:, knee] - ref_knee) ** 2))
Live updates (websocket)#
/ws streams binary frames and JSON events.
A frame is [u32 header length][header JSON][padding to 4 bytes][float32 blob]. The
header carries the instance count, the owner of the scene (robots, data, play,
import), the HUD, model_version and terrain_version, and the offsets of the
overlays in the blob. The blob holds body poses (n, nbody, 7) (position + wxyz), then the
reference ghost poses, then overlay data. When model_version changes, clients fetch
/api/robot/model + /api/robot/model.bin; when terrain_version changes,
/api/scene/terrain.bin ([u32 nv][u32 nf][f32 verts][u8 colours, padded][u32 faces]).
Events are JSON {"type": "event", "topic": …, "payload": …} with topics log,
busy, robot, robots, data, jobs, play, presets, owner and
focus.
See also