Open-weight VLA policies for the SO-101 arm, served over Zenoh. You send observations, you get back a chunk of actions. 1 token per action. Everything on this page was run against the live service on 2026-10-24.
Start here · Your robot · Your own loop · Billing · Errors · Links
Start here
60 seconds. Python 3.10–3.12.
- Login with GitHub. New accounts get 200,000 tokens ($20).
- Dashboard →
Create key. The key is shown once; copy it. - Install and log in:
GIT_LFS_SKIP_SMUDGE=1 pip install agi-diy "lerobot[remote] @ git+https://github.com/huggingface/lerobot@d3793b7e"
agi login # paste the key · verifies it · writes ~/.agi/credentials (chmod 600)
agi-diy has no dependencies of its own. The second package is LeRobot's remote-inference client (zenoh + msgpack).
It lives on lerobot main today — PyPI lerobot 0.6.1 has no remote extra — so the git line is the one that works.
AGI_API_KEY=agi_sk_live_… in the environment replaces agi login.
- Send one call:
agi models
# act/so101 ready slots 2/2 horizon 100 fps 30 so101-degrees-gripper-percent-v1
# smolvla/so101 ready slots 3/3 horizon 50 fps 30 so101-degrees-gripper-percent-v1
agi run act/so101 --probe
# connected tcp/bore.pub:35706 in 320 ms · features observation.state(6,), observation.images.top(480, 640, 3), observation.images.wrist(480, 640, 3) · action 6×100 @ 30 Hz
# chunk 1/3: shape (100, 6) dtype float32 · 296 ms
# chunk 2/3: shape (100, 6) dtype float32 · 63 ms
# chunk 3/3: shape (100, 6) dtype float32 · 96 ms
# billed 300 tokens (1 per action) · `agi usage`
The probe sends zeros for every feature the model expects and prints the chunk it gets back. It costs horizon
tokens per chunk. The first chunk of a session is the slow one (model wake + relay); after that, measured from a Mac
on another network through the public relay: act/so101 63–316 ms, smolvla/so101 436–835 ms.
Your robot
Two models, one arm. Both speak so101-degrees-gripper-percent-v1: six joints in degrees, gripper in percent.
| model | actions per call | fps | cameras (480×640 RGB) |
|---|---|---|---|
act/so101 | 100 | 30 | top, wrist |
smolvla/so101 | 50 | 30 | front, wrist |
Weights: act/so101 = vineetr196/act_so101_pickplace_v1; smolvla/so101 = edge-inference/smolvla-so101-pick-orange
(sim-trained). GET /v1/models is the live version of this table (action_horizon, cameras, slots_free).
Nothing but LeRobot
If your arm already runs with LeRobot, agi rollout leases a slot, fills in the remote-inference flags and runs
lerobot-rollout for you. Camera names must match the model's.
agi rollout act/so101 -- \
--robot.type=so101_follower --robot.port=/dev/tty.usbmodem... \
--robot.cameras='{"top": {"type": "opencv", "index_or_path": 0, "width": 640, "height": 480, "fps": 30},
"wrist": {"type": "opencv", "index_or_path": 1, "width": 640, "height": 480, "fps": 30}}' \
--fps=30 --task="pick up the boat"
lerobot-rollout itself needs lerobot[dataset,feetech] on top of [remote]. The lease ends when the process exits.
strands-robots
import agi
from strands_robots import Robot
session = agi.Policy("act/so101")
arm = Robot("so101", port="/dev/tty.usbmodem...")
arm.run(session.policy(), task="pick up the boat", seconds=30)
session.close()
session.policy() is a strands-robots policy bound to the lease: LeRobot client underneath, JPEG on the wire, client
mode. session.url is zenoh://<host>:<port>/d_…, which strands_robots.Policy(url) also accepts.
Your own loop
Python
import agi, numpy as np
state = np.zeros(6, dtype=np.float32) # shoulder_pan … gripper
top = wrist = np.zeros((480, 640, 3), dtype=np.uint8) # the model's two cameras
with agi.Policy("act/so101") as session:
actions = session.predict(
{"observation.state": state, "observation.images.top": top, "observation.images.wrist": wrist},
task="pick up the boat",
) # np.ndarray [100, 6] — session.action_horizon rows
session.features() lists the names, shapes and dtypes the model expects. Images cross the wire as JPEG
(session.encoding, from the lease): raw RGB through the public relay stalls. Timeouts default to 30 s
(handshake_timeout_s, action_timeout_s, startup_timeout_s); override with session.connect(action_timeout_s=…)
or session.predict(obs, timeout_s=…). connect() tries every endpoint the runner advertised, in order.
For a real control loop take LeRobot's own client: session.connect() returns a connected
lerobot.remote_inference.RemoteClient, session.config() the matching RemoteInferenceConfig.
LeRobot's remote-inference docs apply unchanged.
Any client, over HTTP
A session is a lease on one running model. Create it, point any LeRobot remote-inference client at it, delete it.
curl -s -X POST https://api.agi.diy/v1/sessions \
-H "authorization: Bearer $AGI_API_KEY" -H "content-type: application/json" \
-d '{"model":"act/so101"}'
# {"id":"ses_…","model":"act/so101","state":"leased","deployment":"d_…",
# "zenoh":{"endpoint":"tcp/<relay>:<port>","endpoints":["tcp/…"],"mode":"client","encoding":"jpeg"},
# "semantics":"so101-degrees-gripper-percent-v1","features":{"observation.state":{"shape":[6],"dtype":"float32"},…},
# "action_horizon":100,"fps":30,"connect_by":"…","expires_at":"…","tokens_used":0}
Use zenoh.endpoint + deployment with encoding=jpeg and zenoh_mode=client. Connect within 60 s
(connect_by); the lease lasts ttl_s (default 600, max 3600). DELETE /v1/sessions/<id> when done. Send a real
User-Agent: Cloudflare answers 403 to a bare Python-urllib.
JavaScript
npm i agi-diy
import { createClient } from "agi-diy";
const agi = createClient({ apiKey: process.env.AGI_API_KEY });
const session = await agi.session("act/so101"); // session.endpoint, session.deployment, session.lease.action_horizon
await session.end();
The JavaScript package manages keys, sessions and usage. Inference itself runs through a LeRobot client.
Billing
- 1 call = 1 token per action in the chunk you receive: 100 tokens for
act/so101, 50 forsmolvla/so101. - $0.0001 per token. 200,000 tokens = $20, on every new account.
- Metering is attached to the session, within a second of the call. Nothing is charged for leasing, connecting or idling.
- A session ends when you delete it (
client_done), whenttl_sruns out (expired), when no client connected within 60 s (never_connected), when credits reach 0 (credits), or when the runner disappears (runner_lost). agi usageorGET /v1/usageshows credits, tokens by model and the last 50 events.
agi usage
# credits 198450 tokens · used 1550 tokens in 17 calls
# act/so101 14 calls 1400 tokens
# smolvla/so101 3 calls 150 tokens
Errors
Always {"error": "snake_case", "message": "One sentence."}, plus retry_after_s when waiting helps. The SDK raises
agi.AuthError, agi.InsufficientCredits, agi.UnknownModel, agi.NoSlots (.retry_after_s) and
agi.MissingDependency for the same cases.
| status | error | what to do |
|---|---|---|
| 400 | bad_request | The body is not what the endpoint takes; compare it with the example above. |
| 401 | unauthorized | Send Authorization: Bearer agi_sk_live_…, or log in again; keys you revoked stay revoked. |
| 402 | insufficient_credits | Your balance is 0 tokens; the dashboard shows what is left. |
| 403 | — (Cloudflare page) | Set a User-Agent header; a bare Python-urllib is refused before it reaches us. |
| 404 | not_found | The id or route does not exist, or the session belongs to another account. |
| 404 | unknown_model | No runner serves that model right now; pick one from GET /v1/models. |
| 409 | no_slots | Every slot of that model is leased; retry after retry_after_s seconds. |
| 409 | session_ended | That session is over (end_reason says why); create a new one. |
| 429 | rate_limited | Slow down and retry after retry_after_s seconds. |
| 502 | upstream | The page server did not answer; the API itself is fine, retry. |
| 500 | internal | Our fault; retry once, then check status. |
On the wire: Remote inference request deadline exceeded from LeRobot means the observation never made it in time —
use encoding=jpeg (the SDK does) and a timeout ≥ 30 s for the first call.
Links
- github — source, issues, the full API contract (
docs/CONTRACT.md,docs/API.md). - status — live runner, models and slots, polled every 5 s.
- changelog — what changed, dated.
- PyPI agi-diy · npm agi-diy
- LeRobot remote inference · strands-robots