TypeScriptADK-TS

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

CallbackAvailable onFires
beforeAgentCallbackAll agentsBefore the agent's main logic begins
afterAgentCallbackAll agentsAfter the agent's logic completes
beforeModelCallbackLlmAgent onlyBefore each LLM call
afterModelCallbackLlmAgent onlyAfter each LLM response
beforeToolCallbackLlmAgent onlyBefore each tool executes
afterToolCallbackLlmAgent onlyAfter each tool completes

Return values

What you return from a callback tells the framework whether to proceed or override:

CallbackReturn to continueReturn to override
beforeAgentCallbackundefinedContent — skips the agent, uses this as its output
afterAgentCallbackundefinedContent — replaces the agent's output
beforeModelCallbacknull or undefinedLlmResponse — skips the LLM call
afterModelCallbacknull or undefinedLlmResponse — replaces the LLM's response
beforeToolCallbacknull or undefinedRecord<string, any> — skips the tool, uses this as its result
afterToolCallbacknull or undefinedRecord<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.

Next steps