Stopping
A flow ends when its run returns. Most interesting flows never return, and a Ralph loop is a while True, so you end them from outside. You reach for stopping when a flow is running and you want it to end now.
Try it
Press esc in the interface while a flow is running. The whole flow stops, not just the turn.
An open offers list is dismissed first, if there is one. It is silent when nothing is running.
The three ways to stop
| esc, in the interface | Stops the flow — the whole flow, not just the turn. Dismisses an open offers list first, if there is one. Silent when nothing is running. |
ctrl+c, on a hmz exec command line | The same. |
agent.stop(), from anywhere | The same, for that agent. |
In the interface, ctrl+c works differently. It stops one turn rather than the flow. It stops what is half-typed, if anything, and otherwise the turn of the conversation being read. That conversation is closed under its turn, so the flow reads a turn that failed rather than an agent that was stopped. suppress=True catches it, the agent is still there to take the next turn, and the rest of the flow runs on. esc stops all of it.
What a stop does to the turn under way
The turn is closed out, and every later call into that agent raises Stopped.
A stop leaves the turn where it got to. It does not wait for the turn, because a stop that waited would not read as a stop. A model can think for minutes, and a key that took four of them to have an effect is a key nobody trusts.
A file the agent had half-written stays half-written. A command it had started keeps running until it finishes. What ends is the agent's part in it.
Why suppress=True does not catch a stop
suppress turns a failed turn into an empty answer:
agent(task, suppress=True) # a turn that failed answers ""; the loop goes round againIt deliberately does not catch Stopped. A loop that carried on past a stop would never end:
while True:
agent(task, suppress=True) # ← Stopped comes out of here, and the flow unwindsStopped is not a subprocess.CalledProcessError. Nothing that catches a failed turn catches this by accident. Let it propagate. The cycle then records the run as stopped by hand rather than as one that finished. That is the difference between "it decided it was done" and "somebody pressed esc", and the only place that distinction is written down.
agent.prompted() raises it too, so a run ended while it waited also reads as ended by hand. agent.stopped is the quiet way to ask the same question. It is a bool, and never a raise:
agent.prompted() # waiting for the next thing to say; raises if the wait ended in a stop
agent.stopped # whether it has been told to stop; answers True, and never raisesA hook that raises is normally the hook's own problem. A flow must not fail because something hung off it did. Stopped is the one exception, and it is let out.
What stopping is not
Not /clear. That clears the screen and nothing else. It clears the conversation being read, not the others, and nothing that is running.
Not choosing another flow. /flow is refused while one is running. It refuses with no choosing a flow while a flow is running: esc stops it first. A flow drives the agents it was handed, and it must not have them swapped underneath it. Press esc first, then choose. The page of /flow that chooses one is shut while one runs. Looking and leaving without choosing changes nothing.
Not a question ending. A question still up when the flow ends or is stopped ends with it. Stopping is never blocked on one.
See also
- Talking to a running turn — when a steer is enough
- Being away
- Flows › Stopping