Skip to main content
Hooks run your code before, around and after each prompt, model request and tool call, and on the call’s events. You write them once, and they run the same way on LiveKit and Pipecat.

Add hooks to a call

Build one Hooks at import. Build one CallHooks per call, and pass it to the agent as call=.
  1. Many calls may share one Hooks. It holds only your functions.
  2. A CallHooks binds to one call. Build a new one for each call.
  3. A hook may be sync or async, except a prompt hook, which must be a plain function, and a wrap_* hook, which must be async.

Add a line after every prompt

  1. The suffix goes after the agent’s prompt, every task step’s, the finish checks’ and the summaries’, after a blank line.
  2. With model=, only prompts that model reads get it.
  3. It refuses blank text, more than 512 characters, and a {{...}} placeholder.

Change a model request

  1. request.model names the model that answers, like gpt-5.6-luna. Return None to keep the request as it is.
  2. request.params are extra body fields for this request only.
  3. wrap_model hooks run around the request, and after_model hooks hear the answer.

Refuse or change a tool call

  1. Refuse(message) stops the call. The tool never runs, and the model reads the message.
  2. Return a changed ToolRun to change the arguments. They are checked against the schema again.
  3. wrap_tool hooks run around the tool, as async (run, handler). Call await handler(run) to run it.

Run code when a tool ends

  1. Return None to keep the result. Return something else to replace what the model reads.
  2. An after_tool hook that raises is logged, and the model reads the tool’s own result.

Watch call events

  1. A pattern may hold *, like task.*. A pattern that matches no event raises ValueError.
  2. Observers only watch. One that raises is only logged.
  3. The end of the call waits for call.ended observers, up to end_timeout_s.

Where the model hooks run

The prompt, before_model, wrap_model and after_model hooks run only where the library sends the model request. The tool hooks and the event hooks do not depend on this. When a call has model hooks and no request goes through the library, the adapter logs one warning that says what to change. wrap_model reaches only a request that a ModelCall answers.

Run tool hooks on a plain agent

UnmuteAgent and UnmuteFlows pass the call’s hooks to every tool. On a plain agent, pass them yourself.
  1. hooks=call runs the tool hooks, and fires the tool.* events for your tools.
  2. Framework tools skip the tool hooks: end call, handoffs and native_tools. So do prefetch lookups.

Where to go next

Hooks reference

Every point, event, rule, argument and error.

The call's life

CallHooks, tools on events, the failure line and the end.

Tools

The tools your hooks run around.

Model call

The ModelCall that wrap_model reaches.

Hooks in agent.yaml

Where Unmute declares prompt_suffix.