Skip to main content
CallState holds what one call knows. It keeps typed values, tracks which ones the caller confirmed, and fills them into your prompts. It works on any Pydantic model, on LiveKit and on Pipecat. In the salon tutorial, Remember the caller builds this step by step.

Define what the call remembers

state.py
  1. Every field needs a default, because every value starts the call empty. A field with no default raises TypeError.
  2. confirm maps a value to the task step that must hear the caller agree to it.
  3. Every call needs an agent id. Set UNMUTE_AGENT_ID, or pass call= with an agent_id. With neither, CallState raises SettingsError.

Save a value

  1. save checks the whole batch first. One bad value refuses all of it, and nothing is saved.
  2. A good value is saved in its clean form. The phone number above is saved as +34612345678.
  3. save is the one way in. Read state.values or state.plain("note"), but never write to them.
Task steps, prefetch and the save_values tool all save through the same save.

Ask the caller to confirm a value

  1. A value in confirm is confirmed only when its own step saves it.
  2. Any other save makes it unconfirmed again.
  3. Until it is confirmed, prompts show none recorded yet., so the model never reads a misheard value back as fact. The confirming step still sees it, so it can read it back.

Filling a prompt

  1. {{name}} names a value. {{customer.status}} reads a path into a shape.
  2. A name the state does not hold raises MissingVariablesError.
  3. Call it again after a save, so the prompt shows the new values.
With UnmuteAgent, UnmuteFlows or a ModelCall, you do not call it. The library fills the prompt on every request.

Values the call starts with

  1. call_start names the values the dispatch sets. A required one must be there, or save_call_start raises CallStartError.
  2. dispatched is the call_start object of the dispatch. For a local run, set UNMUTE_CALL_START to the same JSON.
  3. The caller is never asked for these values, and the model cannot change them.
The call’s life shows where save_call_start runs.

Values the call carries: state.call

The library fills state.call from the line. Your State class does not declare it, and only the library writes it. The inbound, outbound and web cases, a test phone call from a web session and the read-only rules are in the State reference.

Saving values no task saves

Some values belong to no task. The model learns them in free talk. Turn on save_values to give it a tool for them.
  1. Each request ends with a short block that lists every value and what it holds now.
  2. The model saves with the save_values tool. null puts a value back to its default.
  3. A value a task saves, or a call-start value, is refused with a reason the model can act on.

Listen to changes

  1. A listener gets one event per save that changed something.
  2. listen(show, replay=True) first sends the values already set.
  3. Call stop() to stop listening. A listener that raises is logged and removed.

Value types

Use these classes from unmute_core.state as field types. Each one cleans and checks the value, and refuses a bad one with a reason the model can act on. Every rule of each type is in the State reference.

Where to go next

State reference

Every argument, rule and error, and State from type text.

Tasks

Steps that save values, and the held save at the end of a call.

Prefetch

Fill values before the greeting.

The call's life

Where call-start values are saved.

Variables on Unmute

How agent.yaml declares the same values.