What it gives you
How it works
rungoes through the entries in order.- A
Clockreads the time in one zone. ACallValuecopies a value fromstate.call. ALookupruns your function or tool. - Each entry saves its values with
state.save. A listener sees them as originprefetch. - The whole block has one budget, 2 seconds by default. Pass
budget=to change it. - When the budget runs out, the rest of the entries are skipped. Their values keep their defaults.
runnever raises. An entry that is skipped, times out or fails is logged and leaves its values at their defaults. The call greets on time.
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.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"].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 tool as a lookup
runmay be aToolSpec. It runs like any tool: locked arguments,{{value}},{{call.name}}and refusal.- Every required argument must be locked. The model never fills one. An open one raises
ValueErrorwhen you build the entry. - A tool lookup has no announce line. Nobody waits for it. A tool with one is refused.
- A tool that is refused or fails counts as a failure. The values keep their defaults.
- A tool that hides its result with
show_result=Falserecords{"ok": true}.
Work beside the greeting
background=True.
Prefetch.runruns only the other entries. It never waits for a background lookup.Prefetch.run_background(state, results=...)runs the background lookups, one after the other.- Each one has its own budget, 10 seconds by default. Pass
background_budget=toPrefetchto change it. - A lookup that fails or runs out of time is logged. Its values keep their defaults.
prefetch.fillsleaves background lookups out. SoGreeting.check(prefetch)refuses a greeting line that names one of their values.CallHooksstartsrun_backgroundfor you. Every model request then waits for it, up tofirst_request_timeout_s. A fixed greeting plays at once.- The prompt shows the value once it is saved.
UnmuteAgentandUnmuteFlowsfill the prompt again before each request.
What each lookup returned
runandrun_backgroundtakeresults. Each lookup that ran records its result as JSON, under the entry name. A lookup that failed records{"error": ...}.CallHookskeeps one ascall.results.UnmuteAgent(call=call)andUnmuteFlows(call=call)give it to their tasks, so a step with atoolinput reads it. In your own host, pass it toCallTasks(call_start=call.results). See Tasks.
Checks when you build it
Passstate=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
call_infobuilds theCallInfoof a phone call. It names the numbers by who called whom.- On an inbound call, the caller is
calling_number. On an outbound call, the agent’s own number is. remote_numberis the other party’s number either way.- A direction other than
inboundoroutboundraisesValidationError.
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
Whatopen_call does with the prefetch, when you give it to CallHooks. See Use it in a call.
open_callrunscall.open(). It runs thebefore_callchecks first.- When the checks pass, it runs
prefetch.run. - Then it starts
prefetch.run_backgroundand the greeting. - Every model request waits for the background lookups, up to
first_request_timeout_s(10 seconds). - A failed check hangs up before any lookup runs. The call’s end, or a failure, stops the background lookups.
- Each lookup’s result goes to
call.results:{"ok": true}for a tool that hides it, and{"error": ...}when it failed.
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 notbackground=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 raisebudget. 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.