> ## Documentation Index
> Fetch the complete documentation index at: https://servo.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Keep your own loop

> Use action chunks in a controller you write, and keep one connection open for a loop that asks many times.

You have run a guarded episode with `session.run`. This page gives you the two calls underneath it, so your own controller can use the same session on the robot computer.

## The two calls

`predict` reads the robot's observation, sends it, and returns the whole chunk for that moment; it keeps no history. `send_action` sends one row to the arms. Your controller decides how many rows to play from a chunk and when to predict again, and the two shapes below cover the common cases. `robot.get_observation()` returns the same `Observation` predict used: `images` by camera role and `state` in the contract's order.

Pace every `send_action` call `chunk.dt_s` seconds apart in both shapes below, the spacing the model assumed between rows; `session.run` does this for you, adaptively.

## Full-chunk playback

Play every row of a chunk before asking for the next one, pacing sends `chunk.dt_s` apart:

```python robot computer theme={null}
import servo

sv = servo.Servo()
robot = sv.robots.attach("yam-cell-01")
model = sv.models.get("hf://allenai/MolmoAct2-BimanualYAM")

deployment = model.deploy()
deployment.wait(timeout_s=900)
session = deployment.start(robot, instruction="pick up the red cup")

chunk = session.predict(robot)
for action in chunk.actions:
    robot.send_action(action)
    # wait chunk.dt_s before the next send_action
```

Fewer requests, but the arm pauses for one round trip at each chunk boundary.

## Receding horizon

Predict again after every row, executing only the newest chunk's first action and discarding the rest:

```python robot computer theme={null}
with deployment.start(robot, instruction="pick up the red cup") as session:
    for _ in range(90):
        chunk = session.predict(robot)
        robot.send_action(chunk.actions[0])
        # wait chunk.dt_s before the next predict
```

This is the shape diffusion-policy- and pi0-style controllers use. One round trip per action, so it only pays off when your round trip is a small fraction of `dt_s` — but every command comes from the freshest observation.

`session.run` does more than either shape: it prefetches the next chunk while the current one plays so the arm never pauses at a boundary, and it blends a short window across the switch instead of cutting over on the row. A loop you drive yourself gets neither unless it implements them, and it must keep the hardware stop within reach.

## A session that keeps its connection

A session holds one connection for one robot and one instruction, and `predict` allows one request in flight at a time; overlapping requests is what `session.run`'s prefetch already handles for you. Leaving the `with` statement closes the session, and closing it never stops the deployment. A new instruction is a new session, since instruction is bound to the session rather than passed on each `predict` call.

## You now have

* A controller that asks for chunks and sends rows itself, in the shape that fits your round trip.
* A session that holds one connection for a loop you drive yourself.

## Next

<CardGroup cols={2}>
  <Card title="Choose a model" icon="cube" href="/guides/models">
    Private buckets, revisions, and what the checkpoint expects.
  </Card>

  <Card title="Identity and auth" icon="users" href="/guides/identity">
    Sign in as yourself, grant a role, and work from your own laptop.
  </Card>
</CardGroup>
