Callbacks
Hook into the ADK-TS agent lifecycle to observe, modify, and control behavior at six defined execution points — without changing framework code.
Callbacks are functions you attach to an agent that the framework calls automatically at six defined points in every run — before and after the agent itself, the LLM, and each tool. They let you add logging, enforce guardrails, modify data, or short-circuit execution without touching framework internals.
Where callbacks fire
Each callback fires at a fixed point in the execution chain. The agent invokes the LLM and tools as needed, and each of those steps has a before/after hook:
Available callbacks
| Callback | Available on | Fires |
|---|---|---|
beforeAgentCallback | All agents | Before the agent's main logic begins |
afterAgentCallback | All agents | After the agent's logic completes |
beforeModelCallback | LlmAgent only | Before each LLM call |
afterModelCallback | LlmAgent only | After each LLM response |
beforeToolCallback | LlmAgent only | Before each tool executes |
afterToolCallback | LlmAgent only | After each tool completes |
Return values
What you return from a callback tells the framework whether to proceed or override:
| Callback | Return to continue | Return to override |
|---|---|---|
beforeAgentCallback | undefined | Content — skips the agent, uses this as its output |
afterAgentCallback | undefined | Content — replaces the agent's output |
beforeModelCallback | null or undefined | LlmResponse — skips the LLM call |
afterModelCallback | null or undefined | LlmResponse — replaces the LLM's response |
beforeToolCallback | null or undefined | Record<string, any> — skips the tool, uses this as its result |
afterToolCallback | null or undefined | Record<string, any> — replaces the tool's result |
You can also mutate inputs in-place before returning the continue sentinel.
For example, modify llmRequest.config.systemInstruction in
beforeModelCallback and return null to proceed with the modified request.
Quick start
Attach callbacks via AgentBuilder. This example logs every LLM call and blocks a specific keyword before it reaches the model:
import { AgentBuilder, CallbackContext, LlmResponse } from "@iqai/adk";
import type { LlmRequest } from "@iqai/adk";
const { runner } = await AgentBuilder.create("my_agent")
.withModel("gemini-2.5-flash")
.withBeforeModelCallback(
({
callbackContext,
llmRequest,
}: {
callbackContext: CallbackContext;
llmRequest: LlmRequest;
}): LlmResponse | null => {
const lastMessage =
llmRequest.contents[llmRequest.contents.length - 1]?.parts?.[0]?.text ??
"";
if (lastMessage.includes("confidential")) {
return new LlmResponse({
content: {
role: "model",
parts: [{ text: "I cannot process that request." }],
},
});
}
console.log(`LLM call from agent: ${callbackContext.agentName}`);
return null;
},
)
.build();All six callbacks accept either a single function or an array. When you pass an array, callbacks run in order until one returns a non-sentinel value — the rest are skipped.