{"slug": "record-train-and-deploy-from-one-place-with-strands-agents-lerobot-and-hugging", "title": "Record, train, and deploy from one place with Strands Agents, LeRobot, and Hugging Face Storage Buckets", "summary": "AWS's open source Strands Robots SDK now integrates with Hugging Face Storage Buckets to enable a continuous data loop for robot learning, allowing users to record demonstrations, train policies, and deploy them to hardware without repeated full dataset transfers. The integration leverages LeRobot's dataset format, already used by over 90,000 datasets and models from more than 8,000 publishers on the Hugging Face Hub, and was announced in March 2026. The workflow is demonstrated in a runnable notebook, examples/notebooks/05_streaming_data_loop.ipynb, and supports robots like the SO-101.", "body_md": "Robotics • 5B • Updated • 7.01k • 21\n\n# Record, train, and deploy from one place with Strands Agents, LeRobot, and Hugging Face Storage Buckets\n\n[Enterprise Article](/blog)\n\n*A walkthrough of the streaming data loop in Strands Robots, one agent loop that records robot demonstrations, trains on them by reading straight from the Hub, and deploys the policy back to hardware, with the dataset in the same on-disk LeRobot format the whole way through.*\n\nYou have an agent that can already record a demonstration and push it to the [Hugging Face Hub](https://huggingface.co/). Now you want to run that loop continuously: collect episodes through the day, train a policy on the growing dataset, deploy it, and pull the next batch back to improve it. Run that loop once and every piece works. Run it every day and you start paying for the same byte transfers over and over. The recordings you upload keep growing, each training run copies the whole dataset to the GPUs before it starts, and every new checkpoint ships out while the next batch of recordings comes back.\n\nThe [first post in this series](https://huggingface.co/blog/amazon/strands-lerobot-hub-to-hardware) introduced [Strands Robots](https://github.com/strands-labs/robots), an open source SDK from AWS ([Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0)) that exposes robot abstractions, simulation, and the [LeRobot](https://github.com/huggingface/lerobot) stack as AgentTools you compose into a single Strands agent. It covered the `Robot()`\n\nfactory, recording a demonstration in simulation, running a policy, and deploying the same agent code to a physical SO-101. That factory resolves a name against a registry of arms, humanoids, mobile bases, and hands, so the SO-100 used throughout this post is one of many supported embodiments. The [robot catalog](https://strands-labs.github.io/robots/robots/) lists every robot the factory knows about. LeRobot's dataset format is already used by over 90,000 datasets and models on the Hub from more than 8,000 publishers ([LeRobot Project Pulse](https://huggingface.co/spaces/imstevenpmwork/lerobot-adoption-dashboard)). A Strands Robots recording is one more of them, so anything built to read LeRobot data can read it without conversion. If you are new to Strands Robots, start there; this post assumes that setup.\n\nThat post followed the agent loop in one direction, from a Hub dataset to a physical robot. This one follows the data the other way, from the first recorded frame back to the deployed policy, over [Hugging Face Storage Buckets](https://huggingface.co/docs/hub/storage-buckets) - a mutable, non-versioned, [Xet](https://huggingface.co/blog/from-files-to-chunks)-backed object-storage repository type [announced in March 2026](https://huggingface.co/blog/storage-buckets). A bucket sits beside your dataset repositories in the same `hf://`\n\nnamespace and uses the `hf`\n\nCLI you already have, so it becomes the working layer that holds your data between the day you record it and the day you train on it.\n\nSomeone has to decide which episodes to keep, when the scene has drifted far enough to re-record, whether today's batch is enough to train on, and which checkpoint replaces the one on the arm. Each of those decisions comes up dozens of times over a collection campaign, and each one needs a look at what came back before the next command goes out. That is the work an agent is for. This post walks you through the data loop inside a single agent: record a demonstration into a Storage Bucket, store it so that each sync uploads only the bytes that changed, train by streaming the dataset straight from the Hub instead of downloading it, and deploy the checkpoint back to hardware with one keyword argument change. The runnable companion to this post lives at [ examples/notebooks/05_streaming_data_loop.ipynb](https://github.com/strands-labs/robots/blob/main/examples/notebooks/05_streaming_data_loop.ipynb).\n\n## What you'll build\n\nWhere the first post recorded a dataset and pushed it to the Hub, the agent you build here records a LeRobotDataset from a natural-language prompt, syncs it into a Storage Bucket, and streams that same dataset back frame by frame, decoding camera video on the fly, with no local copy. You read it back in the same process that wrote it: the same Strands Robots `Robot()`\n\nthat recorded the dataset streams it. Your trained checkpoint then deploys to that same `Robot()`\n\nwith one keyword argument change, and the demonstrations it records on hardware return to the same bucket.\n\n**Figure 1.** *The four stages share one backend.* `Robot(\"so100\")`\n\n*records a LeRobotDataset through the shared* `DatasetRecorder`\n\n; `sync_dataset_to_bucket(...)`\n\n*syncs it into a Storage Bucket;* `stream_dataset(...)`\n\n*reads it back over the Hub with no full download; and the trained checkpoint deploys to the same* `Robot`\n\n*with* `mode=\"real\"`\n\n. *The on-disk format stays exactly as LeRobot wrote it.*\n\nBecause one `Robot()`\n\nboth records a dataset and reads it back, collecting data and training on it are two methods on one object over one backend. The agent decides to run an episode and invokes one tool; the rollout then proceeds at the robot's control frequency until the episode ends, with the trained policy producing every action. The whole loop, in a handful of lines:\n\n``` python\nfrom strands import Agent\nfrom strands_robots import Robot\n\nsim = Robot(\"so100\")                 # mode=\"sim\" (default - safe, no hardware)\nagent = Agent(tools=[sim])\n\n# Record a demonstration and sync it to a bucket.\nagent(\"Record a pick-the-cube demo and sync it to my-org/robot-fave.\")\n\n# Stream it back from the bucket to train, without downloading it first.\nfor batch in sim.stream_dataset(\"my-org/robot-fave/cube_pick\", repo_type=\"bucket\").dataloader(batch_size=64):\n    ...\n```\n\nWhat follows is what's actually happening inside that loop, step by step.\n\n## Prerequisites\n\n#### Minimal (default simulation path)\n\n- Python 3.12+, on Linux or macOS (Apple Silicon supported for the MuJoCo backend).\n- A Strands-compatible model provider for the agent's reasoning.\n[Amazon Bedrock](https://aws.amazon.com/bedrock/)with AWS credentials, the[Anthropic API](https://docs.anthropic.com/), OpenAI, or[Ollama](https://ollama.com/)running locally. - Strands Robots with the dataset extras:\n`uv pip install -U \"strands-robots[sim-mujoco,lerobot]>=0.5.1\"`\n\n. The`lerobot`\n\nextra pulls in LeRobot (>=0.6.1),`datasets`\n\n,`av`\n\n, and`torchcodec`\n\n, so recording and video decode both work without further setup. Refer to[installation guide](https://strands-labs.github.io/robots/getting-started/installation/).\n\nThat's it. Every stage in this post runs on a laptop with these three. What runs is the loop, not a working policy: the default path uses a mock policy, which records a valid dataset but not a useful one.\n\n#### Advanced (buckets, hardware, real policies)\n\n- A Hugging Face account and a token with write permission, plus the\n`hf`\n\nCLI for creating buckets and syncing datasets:`pip install -U \"huggingface-hub>=1.6.0,<2.0.0\"`\n\n, then`hf auth login`\n\n. - For the hardware path: an SO-101 follower and leader pair, or any other LeRobot-supported robot, with calibration files under\n`~/.cache/huggingface/lerobot/calibration/`\n\n. - For local vision-language-action (VLA) inference: an NVIDIA GPU. For training at scale, a GPU cluster reading from the Hub.\n- To run the training step:\n`uv pip install \"lerobot[training]\"`\n\n. Recording and streaming do not need it. If you skip it,`trainer.train()`\n\nreturns an error result rather than a checkpoint. The[troubleshooting guide](https://strands-labs.github.io/robots/troubleshooting/)names that error and the install that fixes it.\n\n## Step 1 - Record a demonstration into a bucket\n\nYou record new episodes through the day, each a continuous run of camera frames and joint state-action telemetry. LeRobot writes that as a small set of large files that grow as you record. Push them into a versioned dataset repository and every append becomes a commit, and every revision is retained. Collection wants the reverse: somewhere to write bytes and overwrite them in place. That is a [Storage Bucket](https://huggingface.co/docs/hub/storage-buckets), which lives inside your Hugging Face workspace and uses the permissions you already have. There are no identity and access management (IAM) roles to configure, no cross-origin resource sharing (CORS) rules, and no upload service to maintain.\n\nYour agent records a LeRobotDataset in the same format LeRobot writes on hardware. Record the episode, then sync the finished dataset into a bucket. The prompt asks for the mock policy, a stand-in that produces joint actions without a trained model, so you can run the whole loop before you have a checkpoint to run:\n\n``` python\nfrom strands import Agent\nfrom strands_robots import Robot, sync_dataset_to_bucket\n\nsim = Robot(\"so100\")                 # mode=\"sim\" by default\nagent = Agent(tools=[sim])\n# One prompt drives scene setup, cameras, policy, and recording.\nagent(\n    \"Create a world with the so100 robot, add a red cube and a front camera, \"\n    \"start recording (repo_id='local/cube_pick', root='/tmp/cube_pick', fps=30, \"\n    \"overwrite=True, task='pick up the red cube'), run the mock policy for \"\n    \"60 steps, then stop recording.\"\n)\n# Sync the finished on-disk dataset into the bucket (no live recording session needed).\nsync_dataset_to_bucket(\"/tmp/cube_pick\", \"my-org/robot-fave\")\n# -> {\"status\": \"success\", \"bucket_uri\": \"hf://buckets/my-org/robot-fave/cube_pick\"}\n```\n\nThe sync writes to `hf://buckets/{bucket}/{run_id}`\n\n, where `run_id`\n\ndefaults to the dataset directory name. The streaming read in Step 3 names the run too: the first two segments of the id are the bucket, and everything after them is the path inside it.\n\n`sync_dataset_to_bucket(root, bucket, run_id=...)`\n\nvalidates the dataset and syncs it through the `hf`\n\nCLI, decoupled from the recording lifecycle. The same capability is on `DatasetRecorder.sync_to_bucket(bucket, run_id=...)`\n\nif you drive an open recorder directly, and `stop_recording(bucket=...)`\n\nsyncs at the moment you stop an active recording. The bucket is the working layer you write to through the day; for the versioned, published artifact you still call `push_to_hub()`\n\n. Both hold the same format.\n\nThe episode is structurally complete, but the actions are placeholders, so it is not training data you would want. Swap in a real policy with `create_policy(\"<hf_repo>\")`\n\nfor actual grasping; the prompt, the format, and the bucket sync stay identical.\n\n#### Recording on hardware\n\nTo record on a physical SO-101, [LeRobot's record CLI](https://huggingface.co/docs/lerobot/il_robots) handles the leader-follower bring-up:\n\n```\nlerobot-record \\\n  --robot.type=so101_follower --robot.id=my_follower \\\n  --teleop.type=so101_leader  --teleop.id=my_leader \\\n  --dataset.repo_id=my_user/cube_picking \\\n  --dataset.single_task='Pick up the red cube'\n```\n\nThe dataset lands on disk in the same format as the simulation recording, so the same sync call takes it to a bucket: `sync_dataset_to_bucket(\"./recordings\", \"my-org/robot-fave\", run_id=\"run-021\")`\n\n(or the `hf sync ./recordings hf://buckets/my-org/robot-fave/run-021`\n\nCLI it wraps). Collection runs append into one place, and your published repositories only get the versions you choose to publish.\n\n## Step 2 - Store with byte-level deduplication\n\nNow that a dataset is in the bucket, the question is what the next sync costs you. Point two fixed cameras at an arm clearing the same table for eight hours and most of what you record is pixels you already have: the same lighting, the same chassis, the same background, across thousands of episodes. On a versioned repository it gets worse, because changing one frame in a multi-gigabyte video shard re-uploads the whole file.\n\nBuckets are backed by [Xet](https://huggingface.co/blog/from-files-to-chunks), which deduplicates your uploads at the byte level using content-defined chunking. Chunk boundaries follow the content, so inserting a few bytes changes only the chunk it lands in instead of shifting every boundary after it. In Hugging Face's own measurements ([HF Storage](https://huggingface.co/storage)), content-defined chunking reduces data transferred per upload by about four times across the Hub, and on Enterprise plans billing is on the deduplicated footprint. Their [bucket benchmarks](https://huggingface.co/spaces/h-m-t/hf-buckets-benchmark) show what that looks like on a single file. Starting from a 500 MB upload, changing 1% of the bytes and re-uploading moved 5.5 MB, changing 5% moved 27.5 MB, and changing 10% moved 55 MB. Without chunk-level deduplication, overwriting an object means sending all of its bytes again, whether or not they changed.\n\nHow much that saves you depends on the file layout, and the Strands Robots recorder uses LeRobot's. Episodes go into Parquet shards (`data/chunk-000/file-000.parquet`\n\n) and per-camera MP4 shards (`videos/observation.images.front/chunk-000/file-000.mp4`\n\n), rolling to a new file only when the current one fills, at LeRobot's defaults of 100 MB for data Parquet and 200 MB for video MP4. So a sync after a day of recording uploads the new trailing shards plus the one partially-filled shard that grew, rather than the whole dataset. Sync the same bucket again tomorrow and Xet handles the deduplication.\n\n**Figure 2.** *A sync uploads only what changed. The first sync of a fresh dataset uploads every chunk; after recording more episodes, Xet's content-defined chunking means the next sync uploads only the new chunks and skips the ones already stored.*\n\n## Step 3 - Train by streaming from the Hub\n\nTo train, you point GPUs at your dataset. Download it first and those GPUs sit idle until hundreds of gigabytes finish copying. Streaming straight from the Hub works here because of the shard layout from Step 2: a batch becomes a few byte-range reads over large shards rather than thousands of small fetches. LeRobot's `StreamingLeRobotDataset`\n\nturns that into a drop-in torch iterable, and Strands Robots exposes it through `stream_dataset()`\n\n:\n\n**Figure 3.** *Stream, don't download. The download path copies the whole dataset to local disk first, so the GPU waits;* `stream_dataset()`\n\n*reads batches straight from the bucket with nothing on local disk, so the GPU trains from the first batch.*\n\n```\nreader = sim.stream_dataset(\"my-org/robot-fave/cube_pick\", repo_type=\"bucket\",\n    shuffle=False, max_num_shards=1, buffer_size=1,  # one episode, in capture order\n)\n\nprint(reader.num_episodes, reader.num_frames, reader.fps)\nfor frame in reader:\n    frame[\"observation.images.front\"]   # (3, H, W) tensor, decoded on the fly from the MP4 shard\n    frame[\"observation.state\"]           # joint vector, from the Parquet shard\n    frame[\"action\"]\n    break\n```\n\nNothing lands on local disk except the small `meta/`\n\nfolder of schema, statistics, and episode index. Camera frames are decoded from the remote MP4 shards as you iterate; state and action come from the Parquet shards. That loop reads one frame at a time, which suits inspecting an episode. To train, pass the reader to a `DataLoader`\n\nand iterate batches instead. The streaming dataset shuffles internally through a bounded reservoir buffer, so video decoding parallelizes across worker processes, and the training step itself is the ordinary PyTorch one:\n\n```\n# policy here is a LeRobot policy you constructed, such as ACTPolicy.\nfor batch in reader.dataloader(batch_size=64, num_workers=4):\n    loss, _ = policy(batch)   # lerobot ACTPolicy.forward returns (loss, loss_dict)\n    loss.backward()\n```\n\nIf you would rather not write the loop at all, LeRobot's own trainer reads through the same engine, so the dataset your agent collected trains without a line of new code. It takes a bucket through the same keyword argument the in-process reader uses:\n\n```\nlerobot-train --policy.type=act \\\n  --dataset.repo_id=my-org/robot-fave/cube_pick \\\n  --dataset.repo_type=bucket \\\n  --dataset.streaming=true \\\n  --num_workers=4\n```\n\nBuckets are streaming-only, so `--dataset.repo_type=bucket`\n\nrequires `--dataset.streaming=true`\n\nand the config rejects the combination otherwise. Reach for `stream_dataset()`\n\nwhen you want the loop in your own process: validating an episode, replaying it in simulation, or feeding a custom evaluation loop. For proprioceptive-only streaming, `drop_videos=True`\n\nskips video decode entirely, which is what makes this work on an edge device with no `torchcodec`\n\nwheel. The [recording and datasets guide](https://strands-labs.github.io/robots/recording/) documents that argument along with the `delta_timestamps`\n\nmap it requires.\n\nProvider names are shared between running a policy and training one. `create_trainer(\"lerobot_local\")`\n\nreturns a `Trainer`\n\nthat works like `create_policy()`\n\n, and a [ TrainSpec](https://strands-labs.github.io/robots/recording/) describes the run; the record-train-deploy loop then closes in a few lines:\n\n``` python\nimport os\nos.environ[\"STRANDS_TRUST_REMOTE_CODE\"] = \"1\"   # create_policy loads with trust_remote_code=True\n\nfrom strands_robots import create_policy\nfrom strands_robots.training import TrainSpec, create_trainer\n\ntrainer = create_trainer(\"lerobot_local\", device=\"cuda\")\nspec = TrainSpec(dataset_root=\"/tmp/cube_pick\", output_dir=\"/tmp/cube_pick_ft\",\n                 base_model=\"\", steps=500, extra={\"policy_type\": \"act\"})\nresult = trainer.train(spec)                    # train ACT on the streamed dataset\npolicy = create_policy(result.checkpoint_dir)   # load the checkpoint straight back\n```\n\nOn a single NVIDIA L4 (`g6.4xlarge`\n\n), 500 optimizer steps of [ACT](https://tonyzhaozh.github.io/aloha/) (51.6M parameters, effective batch size 8) over a 120-frame episode completed in 133 seconds and wrote a checkpoint that `create_policy()`\n\nloads back through the same entry point used to run any other policy. Training time scales with dataset size, batch size, and step count, so treat this as one measured configuration rather than a benchmark. The `\"groot\"`\n\nand `\"cosmos3\"`\n\nproviders target the same `TrainSpec`\n\nand `Trainer`\n\nlifecycle, so the surrounding loop is unchanged; each one validates its own required fields first, so a GR00T run needs a `base_model`\n\nand an `embodiment`\n\ntag, and a Cosmos 3 run needs a `base_model`\n\nand an SFT recipe. Call `trainer.validate(spec)`\n\nbefore `train()`\n\nand it returns the exact list of what a given backend is missing.\n\nHugging Face's pre-warming caches bucket data at edge locations near the cloud and region where your jobs run, so your cluster reads locally and the dataloader stays ahead of the GPU. In Hugging Face's own [bucket benchmarks](https://huggingface.co/spaces/h-m-t/hf-buckets-benchmark), a warm content delivery network (CDN) read hit about 1,086 MB/s on a 10 GB payload against 780 MB/s cold, and roughly 1,124 MB/s warm at 100 GB, measured on an `m5dn.24xlarge`\n\nin `us-east-1`\n\n. The full comparison against plain object storage, upload as well as download, is on that dashboard. Choosing where that data lives is a [Storage Regions](https://huggingface.co/docs/hub/en/storage-regions) setting on Team and Enterprise plans, as of this writing US and EU, with Asia-Pacific and Gulf Cooperation Council (GCC) regions announced as coming; outside those plans repositories are stored in the US.\n\nOn macOS, `import strands_robots`\n\nputs Homebrew's `ffmpeg`\n\non the loader path for you, so `torchcodec`\n\ndecodes streamed video without extra setup.\n\n## Step 4 - Deploy the policy and return data to the loop\n\nIn this step you take the checkpoint you just trained, run it on a physical robot, and record the next round of demonstrations with it. This is the same agent code from the [first post](https://huggingface.co/blog/amazon/strands-lerobot-hub-to-hardware), with one keyword argument changed to `mode=\"real\"`\n\n:\n\n```\nrobot = Robot(\"so100\", mode=\"real\", port=\"/dev/ttyACM0\",\n              cameras={\"front\": {\"type\": \"opencv\", \"index_or_path\": \"/dev/video0\", \"fps\": 30}})\nagent = Agent(tools=[robot])\nagent(\"Pick up the red cube.\")\n```\n\nThe checkpoint runs against the physical arm, and the demonstrations that arm records are saved to disk in the same LeRobot format you started with, ready to sync back to the bucket for the next training run.\n\nIf your data already lives on [Amazon Simple Storage Service (Amazon S3)](https://aws.amazon.com/s3/), none of the format work in this post changes. A LeRobotDataset is a directory of Parquet and MP4 shards, so it stores on Amazon S3 the same as anywhere else, and the recording, training, and deploy steps read that format wherever it sits. What a bucket adds is the Hub-native route: `sync_dataset_to_bucket`\n\nand `stream_dataset(repo_type=\"bucket\")`\n\ntarget `hf://`\n\ndirectly, so you get the sync and the streaming read with no separate storage path to wire up. Both paths run the same loop: Amazon S3 if that is where your data already sits, a bucket if you want the sync and the streaming read without provisioning storage first.\n\nRun the loop again tomorrow and you are recording into that bucket, syncing only the bytes that changed, and streaming those bytes to the GPUs without waiting for a download. The data never leaves the LeRobot format, and it never leaves the Hub.\n\n## Try it using the sample application\n\nThe full Strands Robots sample is on GitHub at [strands-labs/robots](https://github.com/strands-labs/robots) in [ examples/notebooks/05_streaming_data_loop.ipynb](https://github.com/strands-labs/robots/blob/main/examples/notebooks/05_streaming_data_loop.ipynb). It walks you through the full loop cell by cell: record, render, sync to a bucket, stream back, train, and load the checkpoint. Every cell runs in simulation on the mock policy, so no GPU, no Docker, and no Hugging Face credentials are needed.\n\n```\ngit clone https://github.com/strands-labs/robots.git\ncd robots\nuv pip install -U \"strands-robots[sim-mujoco,lerobot]>=0.5.1\"\njupyter notebook examples/notebooks/05_streaming_data_loop.ipynb\n```\n\nRun the cells top to bottom. The recorded dataset lands under `/tmp/nb5_dataset`\n\n. To sync it to a bucket, set `BUCKET = \"my-org/robot-fave\"`\n\nin the first cell (after `hf auth login`\n\n); the neighboring `RUN_ID`\n\nnames the folder inside the bucket, and the notebook streams back from `f\"{BUCKET}/{RUN_ID}\"`\n\n. To train on a GPU, raise `steps`\n\nto 500 and set `device=\"cuda\"`\n\n. The agent-driven version of the same loop lives at [ examples/06_agent_collect_and_stream.py](https://github.com/strands-labs/robots/blob/main/examples/06_agent_collect_and_stream.py).\n\n## Security Considerations\n\nThe snippets here are a \"hello world\" of the Strands Robots data loop. Five things change once you run it against real data.\n\n**Prompt injection.** Supplying untrusted data to an agent can lead to prompt injection, where untrustworthy context is treated as LLM instructions. These agents actuate robots and now also write to and read from shared storage, so this is an important risk to track. Feed the agent only data from trusted sources. If not all input can be trusted, restrict the tools available to the agent so it cannot take safety-critical actions or overwrite bucket contents.**Training data is a trust boundary.** An agent that can write into the collection bucket can also write episodes that a policy later trains on, and that policy drives a physical arm. Keep the credential that writes collection data separate from the one a training job reads with, sync each run under its own`run_id`\n\nso an episode can be traced to the run that produced it and removed on its own, and treat the versioned dataset repository as the reviewed artifact, because the bucket keeps no revisions to audit against.**Bucket credentials and scope.**`sync_dataset_to_bucket(...)`\n\n,`stop_recording(bucket=...)`\n\n, and`sync_to_bucket`\n\nupload through the`hf`\n\nCLI using the token from`hf auth login`\n\n. Use a token scoped to the specific namespace you are writing to, prefer`--private`\n\nbuckets for collection data, and keep the bucket distinct from the versioned dataset repository you`push_to_hub`\n\nand share.**Overwrite in place keeps no revisions.** A bucket overwrites in place and retains no revisions, which is what makes it a working layer and also means a repeated`run_id`\n\nreplaces the run already stored there. Pass an explicit`run_id`\n\nper collection run, as in`sync_dataset_to_bucket(\"./recordings\", \"my-org/robot-fave\", run_id=\"run-021\")`\n\n. For anything you need to be able to return to,`push_to_hub()`\n\nto a versioned dataset repository, where every revision is retained.**Only use trusted Hugging Face orgs.** The local inference path loads Hugging Face models with`trust_remote_code=True`\n\n. Set`STRANDS_TRUST_REMOTE_CODE=1`\n\nto opt in, and only load checkpoints from organizations you trust. When loading pre-trained weights from the Hub (e.g., via`pretrained_name_or_path`\n\n), verify the organization is trusted before loading. Model weights can contain arbitrary code (pickle-based checkpoints). Prefer safetensors-format checkpoints where available.\n\n## Clean up\n\nThe loop leaves a bucket, datasets under `/tmp`\n\n, and a checkpoint on disk. Bucket contents count toward your stored volume, so remove what you no longer need:\n\n```\nhf buckets rm my-org/robot-fave/cube_pick/ --recursive --dry-run  # lists, removes nothing\nhf buckets rm my-org/robot-fave/cube_pick/ --recursive            # --yes skips the prompt\nhf buckets delete my-org/robot-fave                               # takes everything in it\nrm -rf /tmp/cube_pick /tmp/cube_pick_ft /tmp/nb5_dataset /tmp/nb5_ft\n```\n\nStop any training process still on a GPU instance, and stop the instance. If you ran the notebook, substitute its `RUN_ID`\n\n(`nb5_demo`\n\nby default) for `cube_pick`\n\n. Anything you published with `push_to_hub()`\n\nis in a versioned repository and is untouched.\n\n## Where to go from here\n\nThe [Strands Robots documentation](https://strands-labs.github.io/robots/) covers the robot catalog, simulation, policy providers, recording, and the mesh in depth. The [recording and datasets guide](https://strands-labs.github.io/robots/recording/) documents the `DatasetRecorder`\n\nAPI, `sync_dataset_to_bucket`\n\n/ `sync_to_bucket`\n\n, and `stream_dataset`\n\nin full.\n\nIf you collect from more than one robot, give each one its own `run_id`\n\nand they write into the same bucket in parallel. The [multi-robot mesh](https://strands-labs.github.io/robots/mesh/) fans one agent out across those robots, so the same loop becomes a fleet collecting through the day into shared storage. A streaming reader reads one run at a time. The [recording and datasets guide](https://strands-labs.github.io/robots/recording/) describes how to train across several of them.\n\nIf you want a larger policy than ACT, the `TrainSpec`\n\nand `Trainer`\n\nlifecycle from Step 3 covers GR00T and Cosmos 3 behind their own provider names, so fine-tuning a VLA on the dataset you just streamed is the same calls with a different provider string and a base model. Running the result is where the paths diverge, because a VLA checkpoint deploys to hardware rather than to the simulator you trained from. For heavier simulation to generate that data, the Newton (`sim-newton`\n\n) and Isaac Sim (`isaac`\n\n) backends sit behind the same `Robot()`\n\nfactory, so the agent code does not change as you scale up.\n\nBucket streaming reached LeRobot through contributions from both the Strands Robots and LeRobot teams, upstream in LeRobot itself, so the datasets your agent collects are readable by every tool in that ecosystem. That runs both ways: the reader in Step 3 opens any of the LeRobot datasets already published on the Hub, so an agent can replay and evaluate against existing demonstrations before it records one of its own.\n\nContributions are welcome under Apache 2.0. If you build something with this loop, open an issue with what worked and what didn't.\n\n## Resources\n\n**Strands Robots**\n\n**SDK, AgentTools, and the**:`Robot()`\n\nfactory[github.com/strands-labs/robots](https://github.com/strands-labs/robots), Apache 2.0**Documentation**:[strands-labs.github.io/robots](https://strands-labs.github.io/robots/)** Recording and datasets guide**:[strands-labs.github.io/robots/recording](https://strands-labs.github.io/robots/recording/)** The notebook for this post**:- run the full loop cell by cell`examples/notebooks/05_streaming_data_loop.ipynb`\n\n**Strands Agents SDK**:[github.com/strands-agents/harness-sdk](https://github.com/strands-agents/harness-sdk)\n\n**LeRobot and the Hub**\n\n**LeRobot**:[github.com/huggingface/lerobot](https://github.com/huggingface/lerobot)- datasets, policies, hardware drivers** Hugging Face Storage Buckets**:[Storage Buckets documentation](https://huggingface.co/docs/hub/storage-buckets)** Xet deduplication**:[From Files to Chunks](https://huggingface.co/blog/from-files-to-chunks)** A pick-and-place dataset**in the format this post records:[lerobot/svla_so101_pickplace](https://huggingface.co/datasets/lerobot/svla_so101_pickplace)\n\n**Policies**\n\n**SmolVLA**:[lerobot/smolvla_base](https://huggingface.co/lerobot/smolvla_base)** Pi0**:[lerobot/pi0_base](https://huggingface.co/lerobot/pi0_base)** NVIDIA Isaac-GR00T N1.7**:[nvidia/GR00T-N1.7-3B](https://huggingface.co/nvidia/GR00T-N1.7-3B)** NVIDIA Cosmos 3 Nano**:[nvidia/Cosmos3-Nano](https://huggingface.co/nvidia/Cosmos3-Nano)** MolmoAct2**, trained for the SO-100/101:[allenai/MolmoAct2-SO100_101](https://huggingface.co/allenai/MolmoAct2-SO100_101)- loads through`lerobot_local`\n\n, needs the`molmoact2`\n\nextra\n\n**Background**\n\n**First post in this series**:[From the Hugging Face Hub to robot hardware with Strands Agents and LeRobot](https://huggingface.co/blog/amazon/strands-lerobot-hub-to-hardware)**The physical-AI data loop** that this workflow follows:[The Physical AI Data Loop](https://huggingface.co/spaces/imstevenpmwork/LeRobot_and_HF_Buckets#phase-1-collect-and-ingest), Steven Palma, Hugging Face, 2026**Bucket throughput and dedup measurements**:[hf-buckets-benchmark](https://huggingface.co/spaces/h-m-t/hf-buckets-benchmark)", "url": "https://wpnews.pro/news/record-train-and-deploy-from-one-place-with-strands-agents-lerobot-and-hugging", "canonical_source": "https://huggingface.co/blog/amazon/strands-lerobot-streaming-data-loop", "published_at": "2026-08-13 17:16:04+00:00", "updated_at": "2026-08-13 17:17:50.767760+00:00", "lang": "en", "topics": ["robotics", "machine-learning", "artificial-intelligence"], "entities": ["AWS", "Strands Robots", "Hugging Face", "LeRobot", "SO-101", "Hugging Face Storage Buckets", "Xet"], "alternates": {"html": "https://wpnews.pro/news/record-train-and-deploy-from-one-place-with-strands-agents-lerobot-and-hugging", "markdown": "https://wpnews.pro/news/record-train-and-deploy-from-one-place-with-strands-agents-lerobot-and-hugging.md", "text": "https://wpnews.pro/news/record-train-and-deploy-from-one-place-with-strands-agents-lerobot-and-hugging.txt", "jsonld": "https://wpnews.pro/news/record-train-and-deploy-from-one-place-with-strands-agents-lerobot-and-hugging.jsonld"}}