OpenVLA-OFT: the Optimized Fine-Tuning recipe for OpenVLA, as a per-call API
OpenVLA-OFT is not a new robot model. It is a fine-tuning recipe for OpenVLA, the open 7B vision-language-action model from a Stanford-led team, plus the policies that recipe produces. The recipe changed how the model is tuned and how it generates actions. The headline effect, from the paper (Kim, Finn, and Liang, Stanford, February 2025): the LIBERO four-suite average rises from 76.5 to 97.1 percent, and action generation runs 26 times faster. This page explains what changed, what the recipe costs in hardware, which checkpoint to grab, and what a hosted lane would look like. The honest state first: this page is the platform's written record for a metered lane for OpenVLA-OFT. Its row sits in the platform's model data with the rate set. It is on the public list and serving per call, confirmed by the October 26, 2026 requests.
Last verified: 2026-10-05
What it is
Start with the base. OpenVLA is a 7 billion parameter vision-language-action model. It was trained on 970,000 real robot episodes from the Open X-Embodiment collection. Its eyes are a fused DINOv2 and SigLIP encoder, its core is a Llama 2 language model. Give it camera images and a task sentence, and it outputs robot actions. The base paper (June 2024) reports it beating the closed RT-2-X 55B model by 16.5 points across 29 tasks, with 7 times fewer parameters.
OpenVLA had one weakness: it generated actions like text, one token at a time. That is slow, and fine-tuning it well was an open problem. OFT, short for Optimized Fine-Tuning, is the answer from the same lead author. It is a recipe with four ingredients, applied on top of the same base weights.
| Fact | Value | Owner of the number |
|---|---|---|
| What it is | the Optimized Fine-Tuning recipe plus the policies it produces | project page |
| Base model | OpenVLA, 7B class, MIT license | base paper |
| Released size | 7,541,237,184 parameters in the checkpoint, same count as base OpenVLA | Hugging Face model files, read 2026-10-05 |
| Recipe | parallel decoding, action chunking, continuous actions, L1 regression | the OFT paper |
| Action chunk | 8 steps on LIBERO, 25 on ALOHA | the OFT paper and project FAQ |
| Throughput | 4.2 to 109.7 action generations per second on one A100 | the OFT paper, Table II |
| LIBERO average | 97.1 percent, against 76.5 for fine-tuned base OpenVLA | the OFT paper, Table I |
| License | MIT, code and all five checkpoints | repository and hub license files, read 2026-10-05 |
OpenVLA-OFT versus base OpenVLA: what actually changed
No dedicated page answers this question, which is odd, because it is the question the search box asks. Here is the full answer in plain words.
What stays the same:
- The backbone. The vision encoder, the language model core, the weight count. OFT is not a new pretrained model.
- The inputs at minimum: one camera image and a task sentence.
- The license. Both the base and OFT code carry MIT.
What changed:
- Parallel decoding. Base OpenVLA predicts actions one token at a time, left to right. OFT predicts all action values of a chunk in a single forward pass. Speed is the main win.
- Action chunking. The base predicts one time step per pass. OFT predicts a run of future steps together: 8 on LIBERO, 25 on ALOHA. The robot executes the chunk while the next one is computed.
- Continuous actions. The base sorts each action dimension into 1 of 256 bins, like words. Bins lose fine detail. OFT regresses the exact value.
- The L1 objective. The action head is a small MLP trained with an L1 loss. The paper's finding: this simple loss filters noise from imperfect human demonstrations, where a fancier diffusion head can faithfully reproduce the demonstrator's mistakes.
The paper decomposes the gain: decoding and chunking together add 14 points of average success over the base fine-tune, continuous actions add about 5 more. The suite-by-suite table lives in the Benchmarks section below. In the LIBERO comparison, OFT adds inputs the base model never took: a wrist camera, and the robot's own joint state.
One more name to decode. OFT+ means the recipe with FiLM, a conditioning trick that injects the language signal into visual features. It matters on real robots with distracting scenes, and it is what drives the bimanual ALOHA results.
The license position is simple: MIT on the code, MIT on every checkpoint. Nothing gated, nothing pending. Comparing robot model licenses in general, including the encumbered ones? The GR00T license guide in our docs walks that decision.
The posture on this platform, stated plainly: the row is on the public list and serves per call. The October 26, 2026 requests confirm it. The models index is the live catalog, and the VLA category page covers the platform's action model family.
Use cases
OFT is the fine-tune specialist of the open VLA family. It is the strongest documented answer to one specific question: I have a robot and a few hundred recordings of my own task. How do I get a 7B-scale policy that actually works on it?
- Adapting a strong base to your arm. The recipe exists to be tuned on your demonstrations. LoRA keeps the memory bill sane and the base model's knowledge intact.
- Simulation benchmarking on LIBERO. The four public suite checkpoints are the reference artifacts. Download, evaluate, compare.
- Fast control from a big model. 109.7 generations per second on one A100 turns a 7B model into a real-time option for a single arm.
- Contact-rich manipulation. Continuous actions have no bin edges. The last millimeters of an insertion or a scoop behave better.
- Bimanual work, with OFT+. The FiLM variant steered two arms at 25 Hz on cloth folding and scooping tasks in the paper's real-robot runs.
Where it is the wrong tool: fast reactive loops inside a chunk (the chunk runs open loop), zero-shot generalist deployment (every checkpoint is tuned to one suite or robot), and simple no-language tasks. For those tasks a small policy like ACT or SmolVLA trains in minutes on a laptop-class GPU. Those siblings have their own pages in this family.
The fine-tuning recipe: LoRA, one model, one suite
The reference recipe from the repository:
- LoRA with rank 32, attached across the model's linear layers per the training script’s all-linear setting. The pretrained weights stay frozen; only the adapters train.
- A small MLP action head for continuous actions, and a projector that feeds the robot's joint state into the model.
- Two camera inputs: a third-person view plus a wrist view.
- Training runs of 50,000 to 150,000 steps. The authors used 8 GPUs of 80 GB for one to two days per suite. Fewer GPUs work with gradient accumulation, just slower.
The hardware math matters more than the steps. The project FAQ lists the minimum training memory at batch size 1: 25.6 GB for one camera, 25.7 GB with a wrist camera and state. Recommended sizes at practical batch sizes: 44.1 to 62.5 GB. That is the cliff. A 24 GB consumer card serves this model happily but cannot start a fine-tune, even at batch 1. A single 32 GB card can, slowly. Inference needs 15.9 to 18.0 GB depending on cameras.
Two practical warnings from the repository's own notes. Train and test on the same GPU class, or merge the LoRA weights into the base model on the device you will test on; otherwise success can drop sharply. And at test time use a center crop, because training used random crops. The LIBERO datasets used are the "no noops" variants: episodes with near-zero actions filtered out.
Norm stats and the unnorm key
Every checkpoint carries normalization statistics for the dataset it was tuned on. Those numbers translate the model's output into your robot's units. Serving code names them with an unnorm key, for example libero_spatial_no_noops. The law is simple: the key must match the checkpoint's dataset. Wrong stats produce wrong motion, the physical kind. The check is enforced in the official evaluation code, and stats mix-ups come up repeatedly in the repository's issue tracker. The lane's contract on this platform returns the stats it applied with every answer, so your driver can check or undo the scaling. The endpoint's stats contract is documented in the action route notes.
API usage
The lane's contract speaks the platform's action route. It is POST /v1/action, with a Bearer key from the console. Multipart form data goes in. JSON comes out. The model id is openvla-oft.
| Form field | Required | Notes |
|---|---|---|
model | yes | openvla-oft |
instruction | yes | the task sentence, non-empty on every row |
state | yes | a JSON array of numbers: your robot's joint positions. The empty array [] is the legal stateless case |
image | yes, one or more | camera frames as file parts. One to three in practice: an outer view plus a wrist view |
One served request returns three things. actions is the action chunk, a list of action vectors in the model's own shape. normalization is the norm stats block the endpoint applied, passed through untouched, so your driver can convert back to your units. usage is ours: the call count and the cost of the call, returned with every answer. Metering is per call, not per token. One served request is one billed call. A refused request bills nothing. The prepaid wallet balance is the spending stop. Every field, error code, and limit lives in the run-action reference.
The path this page exists for: fine-tune the recipe on your robot, then serve the result without owning the serving stack. Write your client against the contract below now. The tabs show the exact shape the lane ships under.
curl
curl -X POST "https://api.ironstratum.com/v1/action" \
-H "Authorization: Bearer $KEY" \
-F "model=openvla-oft" \
-F "instruction=put the black bowl on the stove" \
-F 'state=[0.0, 0.12, -0.4, 0.0, 0.5, 0.1, 0.0]' \
-F "image=@scene.jpg" \
-F "image=@wrist.jpg"
python
import os
import requests
resp = requests.post(
"https://api.ironstratum.com/v1/action",
headers={"Authorization": "Bearer " + os.environ["KEY"]},
data={
"model": "openvla-oft",
"instruction": "put the black bowl on the stove",
"state": "[0.0, 0.12, -0.4, 0.0, 0.5, 0.1, 0.0]",
},
files=[
("image", ("scene.jpg", open("scene.jpg", "rb"), "image/jpeg")),
("image", ("wrist.jpg", open("wrist.jpg", "rb"), "image/jpeg")),
],
timeout=30,
)
resp.raise_for_status()
answer = resp.json()
chunk = answer["actions"] # the action chunk: future steps in the model's own shape
stats = answer["normalization"] # the norm stats this endpoint applied
print("steps in this chunk:", len(chunk))
print("cost of this call:", answer["usage"].get("cost"))
lerobot
import os
import requests
from lerobot.cameras.opencv import OpenCVCamera
session = requests.Session()
session.headers["Authorization"] = "Bearer " + os.environ["KEY"]
cam = OpenCVCamera(camera_index=0, fps=30, width=640, height=480)
cam.connect()
instruction = "put the black bowl on the stove"
state = [0.0] * 7 # your arm's joint positions, same order on every call
running = True
while running:
frame = cam.async_read()
resp = session.post(
"https://api.ironstratum.com/v1/action",
data={
"model": "openvla-oft",
"instruction": instruction,
"state": "[" + ", ".join(str(s) for s in state) + "]",
},
files={"image": ("scene.jpg", frame, "image/jpeg")},
timeout=30,
)
resp.raise_for_status()
chunk = resp.json()["actions"]
for step in chunk:
state, running = arm_step(state, step) # your arm driver goes here
if not running:
break
The loop is the whole pattern. Send frames, instruction, and state. Receive a chunk. Run it at your control rate. Send the new state with the next call. The contract is the form fields, not a client library.
Benchmarks
Every number below is from the authors' paper and project page, named and dated. Nothing is this platform's own measurement: this page carries the publishers' numbers. This platform publishes its own numbers only after they are measured and held, and this page carries none.
LIBERO task success, suite by suite. LIBERO spans 130 tasks in total. The four suites used here are Spatial, Object, Goal, and Long, 10 tasks each, with 500 recorded demonstrations per suite. Inputs for the pi0 and OpenVLA-OFT rows: third-person camera, wrist camera, robot state, language, on filtered training sets. The base OpenVLA row is the paper's single-camera comparison: one image and the task sentence. 500 trials per suite, on an A100, averaged over three seeds.
| Policy | Spatial | Object | Goal | Long | Average |
|---|---|---|---|---|---|
| Base OpenVLA, fine-tuned (single camera) | 84.7 | 88.4 | 79.2 | 53.7 | 76.5 |
| pi0, fine-tuned | 96.8 | 98.8 | 95.8 | 85.2 | 94.2 |
| OpenVLA-OFT | 97.6 | 98.4 | 97.9 | 94.5 | 97.1 |
The long-horizon suite is where the recipe pays: 53.7 to 94.5. Chunking holds a long task together. In the input-matched single-camera rows, OFT scores 95.3 against Diffusion Policy at 72.4, Octo at 75.1, and DiT Policy at 82.4. The combined-suites checkpoint averages 96.8, a hair under the per-suite set.
Inference efficiency, one A100, 100 queries.
| Configuration | Generations per second | Latency |
|---|---|---|
| Base OpenVLA | 4.2 | 239.6 ms |
| Plus parallel decoding | 15.9 | 62.9 ms |
| Plus chunking | 108.8 | 73.5 ms |
| Full OFT (continuous, L1) | 109.7 | 72.9 ms |
| Full OFT, wrist camera and state added | 71.4 | 112.0 ms |
That table is the honest shape of the 26 times claim: the top configuration is the single-camera one, and the extra inputs that lift success to 97.1 cost about a third of the rate. For scale, a diffusion action head in the same framework needed 1.9 seconds per query at full quality and collapses to zero success at a single denoising step.
Real robot, bimanual ALOHA. With FiLM (OFT+) driving two arms at 25 Hz on fold-and-scoop tasks, the paper reports the highest average among the compared methods, up to 15 points absolute over fine-tuned pi0 and RDT-1B and over ACT and Diffusion Policy trained from scratch. Language steering was the hard part. The pot task collected 300 demonstrations, and the authors note the large set was not what fixed language following; the FiLM conditioning was. On the scooping task, with 45 demonstrations, the OFT policy hit 100 percent while a diffusion-tuned VLA reproduced the demonstrator's too-deep spoon habit.
Two honesty notes. Reproduction varies with hardware: the repository warns that results shift across GPU types, and one community run on an A6000 landed above paper on one suite. And the 97.1 figure is a simulation benchmark score, not a promise about your robot. Real-robot results depend on your embodiment, your cameras, and your data. The worked example of that whole path, from recording your own demonstrations to serving the tuned policy, is the SO-101 cloud brain guide in our docs.
Getting started
- Read the primaries. The paper, the project page, and the repository are all public and MIT licensed. Every identity fact on this page traces to them.
- Pick your checkpoint before your hardware. Simulation on a named suite: the matching suite checkpoint. Your own robot: the base weights plus the recipe, tuned on your recordings. Then check the memory table above against your GPU. Serving fits a 24 GB card. Fine-tuning starts at about 26 GB.
- Write your client against the contract now. The tabs above are the shape the lane ships under. The run-action reference documents every field and error. The models index is the live catalog.
- Open the console when you build. The signup flow opens the wallet, the keys, and the billing history. The loaded balance is the spend bound. Each key revokes alone.
Related pages in this family: the VLA category page explains the model class and the call contract, pi05 covers the pi 0.5 family with its DROID and LIBERO tunes, and MolmoAct2 for SO-100 and SO-101 is the low-cost-arm option in the same lane set.