Skip to main content
This page is the full detail behind Prefetch. Start there to fill the call state before the greeting. Come here for every entry, argument, check and rule.

What it gives you

How it works

  1. run goes through the entries in order.
  2. A Clock reads the time in one zone. A CallValue copies a value from state.call. A Lookup runs your function or tool.
  3. Each entry saves its values with state.save. A listener sees them as origin prefetch.
  4. The whole block has one budget, 2 seconds by default. Pass budget= to change it.
  5. When the budget runs out, the rest of the entries are skipped. Their values keep their defaults.
  6. run never raises. An entry that is skipped, times out or fails is logged and leaves its values at their defaults. The call greets on time.
In the guide’s example, find_customer takes the call state and returns a mapping, like {"name": "Ana Ruiz"}. It may be a plain function or an async one. After run, booking_date, weekday, caller_phone and customer_name hold their values. The agent greets with them.

Prefetch

The block of entries a call runs as it starts.
Sequence[Clock | CallValue | Lookup]
required
The entries, run in order. Names must be unique.
type[BaseModel] | None
default:"None"
The State model. With it, every entry is checked at build time against the types of the values it saves.
float
default:"2.0"
Seconds the entries before the greeting may take together. Past it, the rest keep their defaults.
float
default:"10.0"
Seconds each background lookup may take.
state, budget and background_budget are keyword-only.

Clock

Clock(name, timezone, assign) reads the clock once, at the start of the call. A call that crosses midnight keeps the day it started on.
str
required
The entry name, used in log lines.
str
required
An IANA zone, like Europe/Madrid. It is never the container’s clock.
Mapping[str, str]
required
State value name to the clock field it gets, like {"booking_date": "date"}. It may not be empty.
The fields are date, time, datetime, day_of_week, year and timezone. A date reads 2026-10-13, a time reads 09:30, and a day_of_week reads Tuesday. A wrong zone raises ValueError when you build the entry: 'Mars/Base' is not an IANA time zone. So does a wrong field, like 'dat' is not a clock field.

CallValue

CallValue(name, source, assign) copies a value from state.call into a value of your own. state.call is read-only. A copy lets the caller confirm the number or correct it. See State.
str
required
The entry name, used in log lines.
str
required
The state.call field to read: calling_number, called_number, remote_number, direction, channel, carrier_call_id, agent_id or session_id.
Sequence[str]
required
The state value names that get the value. They all get it. A bare string raises TypeError, so write ["caller_phone"].
A call value that is empty counts as absent, and the entry is skipped. A withheld caller ID, like anonymous, is already None in state.call. So it is skipped too.

Lookup

Lookup(name, run, assign, inputs=(), needs_confirmed=(), background=False) runs a function or a tool and saves fields of its result.
str
required
The entry name, used in log lines and as its label in the results.
ToolSpec | Callable[[CallState], object]
required
A function that takes the call state, or a ToolSpec. It returns a mapping. A plain function runs in a thread, so a blocking call cannot delay the greeting.
Mapping[str, str]
required
State value name to a top-level key of the result. A tool lookup may leave it empty. Its result then only reaches a step’s tool input.
Sequence[str]
default:"()"
Value paths the lookup reads. The entry is skipped while one is empty. When an input changes later, the saved values go back to their defaults. So do values looked up from them.
Sequence[str]
default:"()"
Values that must not be waiting for the caller. The entry is skipped while one is.
bool
default:"False"
True runs the lookup beside the greeting.
A result that is not a mapping counts as a failure.

A tool as a lookup

  1. run may be a ToolSpec. It runs like any tool: locked arguments, {{value}}, {{call.name}} and refusal.
  2. Every required argument must be locked. The model never fills one. An open one raises ValueError when you build the entry.
  3. A tool lookup has no announce line. Nobody waits for it. A tool with one is refused.
  4. A tool that is refused or fails counts as a failure. The values keep their defaults.
  5. A tool that hides its result with show_result=False records {"ok": true}.

Work beside the greeting

A slow lookup should not delay the greeting. Mark it background=True.
  1. Prefetch.run runs only the other entries. It never waits for a background lookup.
  2. Prefetch.run_background(state, results=...) runs the background lookups, one after the other.
  3. Each one has its own budget, 10 seconds by default. Pass background_budget= to Prefetch to change it.
  4. A lookup that fails or runs out of time is logged. Its values keep their defaults.
  5. prefetch.fills leaves background lookups out. So Greeting.check(prefetch) refuses a greeting line that names one of their values.
  6. CallHooks starts run_background for you. Every model request then waits for it, up to first_request_timeout_s. A fixed greeting plays at once.
  7. The prompt shows the value once it is saved. UnmuteAgent and UnmuteFlows fill the prompt again before each request.

What each lookup returned

  1. run and run_background take results. Each lookup that ran records its result as JSON, under the entry name. A lookup that failed records {"error": ...}.
  2. CallHooks keeps one as call.results. UnmuteAgent(call=call) and UnmuteFlows(call=call) give it to their tasks, so a step with a tool input reads it. In your own host, pass it to CallTasks(call_start=call.results). See Tasks.

Checks when you build it

Pass state=State to Prefetch. Each entry is then checked at once, not during a call. Each check raises ValueError and names the entry. Every entry also refuses an empty assign with ValueError, because it would save nothing. Clock, CallValue, Lookup and Prefetch are Pydantic dataclasses. A value of the wrong type raises a ValidationError in Pydantic’s wording. prefetch.state is the State model you passed. Prefetch compares by identity, so two prefetches with the same entries are two objects. Lookup and Clock copy the assign mapping you give them, so changing your dict later changes nothing. prefetch.fills is the set of values the entries fill before the greeting. Greeting.check(prefetch) uses it to refuse a greeting that names a value no entry fills. See Greeting. A value reaches the model whole. Only the SLNG Context Router cuts a long template variable. See SLNG.

call_info

  1. call_info builds the CallInfo of a phone call. It names the numbers by who called whom.
  2. On an inbound call, the caller is calling_number. On an outbound call, the agent’s own number is.
  3. remote_number is the other party’s number either way.
  4. A direction other than inbound or outbound raises ValidationError.
LiveKit and Pipecat have their own builders: sip_call_info and telephony_call_info. See Telephony. Pass the result to CallState(State, call=call).
str
required
inbound or outbound.
object
default:"None"
The other party’s number: the caller, or the one dialled.
object
default:"None"
The agent’s own number, its trunk.
str
default:"phone"
phone for a phone line. web to simulate a phone call in a web session.
object
default:"None"
The carrier’s id of the call.
str | None
default:"None"
The agent’s id.
str | None
default:"None"
The call’s id. A new one when None.
call_phone_region(state.call) gives the call’s country, like "ES", or None for a call without numbers. Use it with use_phone_region, so the state can read a national number. See What a call carries.

In a call

What open_call does with the prefetch, when you give it to CallHooks. See Use it in a call.
  1. open_call runs call.open(). It runs the before_call checks first.
  2. When the checks pass, it runs prefetch.run.
  3. Then it starts prefetch.run_background and the greeting.
  4. Every model request waits for the background lookups, up to first_request_timeout_s (10 seconds).
  5. A failed check hangs up before any lookup runs. The call’s end, or a failure, stops the background lookups.
  6. Each lookup’s result goes to call.results: {"ok": true} for a tool that hides it, and {"error": ...} when it failed.
On a plain agent, run await prefetch.run(state) yourself before the session starts. Then you have no background lookups. A native LLM reads the values with render_prompt(PROMPT, state). See State and The order.

Troubleshooting

ValueError names an entry when you build the Prefetch

The entry assigns a value the State does not declare, a value of a type the reading does not fit, or a shape or list. Two entries may share a name too. Fix: read the message. It names the entry and the value. Declare the value, change its type, or give the entry a new name.

Greeting.check refuses the line

The greeting names a value that no entry fills before the greeting. A background lookup does not count. Fix: fill the value with an entry that is not background=True, or take it out of the greeting line.

A value stays at its default

The entry was skipped, ran out of time, or failed. The log names the entry and says why. An input may have been empty, or a needed value may still wait for the caller. Fix: fix the failing entry, or raise budget. Move a slow lookup to background=True.

Where to go next

Prefetch

Fill the call state before the greeting, the short way.

Call lifecycle reference

The order of a whole call, the greeting and every CallHooks argument.

Tasks

A step that reads a lookup’s result.

State

How saved values read in a prompt, and when the caller confirms them.