Turns and the turn record
A turn is the work the agent does to answer one message. The SDK runs it, and gives you its record: one value that says how the turn ended, or what it waits for. You start a turn, wait for it, and read its record. Three calls start a turn:
| Call | What it does |
|---|---|
session.run(message) | Starts a turn and waits for its record. |
session.start_turn(message), then turn.result() | Starts a turn and returns its handle at once. turn.result() waits for the record. |
session.send_message(message) | Starts a turn, or adds the message to the turn that runs. |
Start a turn
reviewer.py is the payment reviewer of What an agent is. report shows what each record holds:
import asyncio
from mistralai import agents
from reviewer import payment_reviewer
def report(state: agents.TurnState) -> None:
match state:
case agents.Completed(tool_calls=calls):
print("completed:", [call.name for call in calls])
case agents.Blocked(callbacks=callbacks):
print("waits for:", [callback.title for callback in callbacks])
case agents.Interrupted(cause=cause):
print("stopped:", cause)
case agents.Failed(error=error):
print("failed:", error.message)
case agents.Running():
print("still running")
case _: # agents.UnknownOutcome: an outcome added after your SDK version
print("ended:", type(state).__name__)
async def main() -> None:
session = await payment_reviewer.start_session()
turn = await session.start_turn(
"Review transaction monitoring alert 4711.",
idempotency_key="alert:4711:review",
)
if isinstance(turn, agents.Busy):
turn = turn.turn # another turn runs: follow it
state = await turn.result()
report(state)
state = await session.run("Which case did you open? Reply with the case id only.")
if isinstance(state, agents.Completed):
print(state.text)
report(state)
await session.cancel()
asyncio.run(main())The second turn called no tool. A tool you wrote shows as client.<name>: see Tool namespaces.
When a turn already runs
A session runs one turn at a time. While a turn runs, start_turn() and session.run() start nothing and return agents.Busy, whose turn is the running turn's handle, as in main() above. send_message() adds the message to the running turn instead.
The turn record
Every call that reads or waits for a turn returns the same record: turn.result(), turn.get(), session.run(), agent.run(), and the state of a TurnEnded event. Match on its type:
| Record | What happened | What you do next |
|---|---|---|
agents.Completed | The turn ended normally. | Read output or text, and send the next message. |
agents.Blocked | The turn waits for an answer, such as a tool approval. | Answer each of its callbacks: see Approvals. |
agents.Running | Your wait ended before the turn did. | Wait again with turn.result(after=state). |
agents.Interrupted | Someone or something stopped the turn. | Send the next message, unless the session has ended. |
agents.Failed | The turn ended with an error. It is returned, not raised. | When error.retryable is True, send the message again: see When a turn fails. |
Keep a case _: for agents.UnknownOutcome, an outcome added after your SDK version. Every record also has session_id, turn_id, cursor and text, the turn's latest answer text. Turn records lists the fields of each type, and the stop reasons of a Completed turn.
Wait with a timeout
timeout= bounds your wait, not the turn. At the deadline, the call returns the record as it is then, often Running, and the turn keeps running. Wait again with turn.result(after=state): see Turn.result.
To stop a turn, call await turn.interrupt(). To end the session, call await session.cancel().
Next
- Events: follow a turn step by step while it runs.
- History entries: read a turn's entries, or start a session from existing history.
- Approvals: answer a
Blockedturn's approvals.