> ## Documentation Index
> Fetch the complete documentation index at: https://docs.braintrust.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Google ADK (Agent Development Kit)

> Trace Google ADK agents in Braintrust to debug prompts, tool calls, and multi-step reasoning

[Google ADK (Agent Development Kit)](https://google.github.io/adk-docs/) is Google's framework for building AI agents powered by Gemini models. Braintrust traces ADK agent executions, including tool calls and multi-step reasoning.

**TypeScript**

## Setup

Install the Braintrust and `@google/adk` packages, then set your API keys. Auto-instrumentation supports `@google/adk` v0.6.1 to v0.6.x and v1.0.0 to v1.x. Versions v0.7.0 through v0.9.x aren't supported.

**1. Install packages**

```bash pnpm theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
pnpm add braintrust @google/adk
```

```bash npm theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
npm install braintrust @google/adk
```

**2. Set environment variables**

```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
BRAINTRUST_API_KEY=<your-braintrust-api-key>
GEMINI_API_KEY=<your-gemini-api-key>

# For organizations on the EU data plane, use https://api-eu.braintrust.dev
# For self-hosted deployments, use your data plane URL
# BRAINTRUST_API_URL=<your-braintrust-api-url>
```

## Auto-instrumentation

To trace ADK agents without modifying your application code, initialize Braintrust normally, then run your app with Braintrust's import hook to patch the ADK SDK at runtime.

**1. Initialize Braintrust and call ADK**

```javascript title="trace-adk-auto.js" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import { initLogger } from "braintrust";
import * as adk from "@google/adk";

initLogger({
  projectName: "my-adk-project",
  apiKey: process.env.BRAINTRUST_API_KEY,
});

const { LlmAgent, InMemoryRunner, FunctionTool } = adk;

// Create the tools
const getWeatherTool = new FunctionTool({
  name: "get_weather",
  description: "Get weather for a city.",
  parameters: {
    type: "object",
    properties: {
      city: {
        type: "string",
        description: "The city to look up weather for",
      },
    },
    required: ["city"],
  },
  execute: ({ city }) => {
    return { temperature: 72, condition: "sunny", city };
  },
});

const getCurrentTimeTool = new FunctionTool({
  name: "get_current_time",
  description: "Get the current time.",
  parameters: {
    type: "object",
    properties: {},
  },
  execute: () => {
    return new Date().toLocaleTimeString("en-US", {
      hour: "numeric",
      minute: "2-digit",
      hour12: true,
    });
  },
});

// Create the agent
const agent = new LlmAgent({
  name: "weather_time_assistant",
  model: "gemini-2.5-flash",
  instruction: "You are a helpful assistant that can check weather and time.",
  tools: [getWeatherTool, getCurrentTimeTool],
});

// Create the runner and a session
const runner = new InMemoryRunner({ agent, appName: "weather_app" });
const userId = "user123";
const sessionId = "session123";
await runner.sessionService.createSession({
  appName: runner.appName,
  userId,
  sessionId,
});

// Run the agent
for await (const event of runner.runAsync({
  userId,
  sessionId,
  newMessage: {
    role: "user",
    parts: [{ text: "What's the weather like in New York?" }],
  },
})) {
  console.log(event);
}
```

**2. Run with the import hook**

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
node --import braintrust/hook.mjs trace-adk-auto.js
```

The auto-instrumentation example uses plain JavaScript so `node --import` can run the file directly. The Braintrust APIs work the same in TypeScript projects. Compile your TypeScript to JavaScript and run the compiled file with the import hook, or [run TypeScript directly](https://docs.braintrust.dev/instrument/trace-llm-calls.md#auto-instrumentation) with a TypeScript loader.

> [!NOTE]
>
> If you're using a bundler, see [Trace LLM calls](https://docs.braintrust.dev/instrument/trace-llm-calls.md#auto-instrumentation) for plugin and loader setup.

## Manual instrumentation

To trace the ADK module manually, wrap it yourself with `wrapGoogleADK()` and destructure the wrapped classes from the result.

> [!NOTE]
>
> Manual instrumentation traces runner, agent, and tool spans, but not LLM call spans. ADK constructs its model client internally, so user-side wrapping cannot intercept the underlying model calls. To capture LLM spans, use auto-instrumentation instead.

```javascript title="trace-adk-wrap.js" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import { initLogger, wrapGoogleADK } from "braintrust";
import * as adk from "@google/adk";

initLogger({
  projectName: "my-adk-project",
  apiKey: process.env.BRAINTRUST_API_KEY,
});

const { LlmAgent, InMemoryRunner, FunctionTool } = wrapGoogleADK(adk);

// Create the tools
const getWeatherTool = new FunctionTool({
  name: "get_weather",
  description: "Get weather for a city.",
  parameters: {
    type: "object",
    properties: {
      city: {
        type: "string",
        description: "The city to look up weather for",
      },
    },
    required: ["city"],
  },
  execute: ({ city }) => {
    return { temperature: 72, condition: "sunny", city };
  },
});

const getCurrentTimeTool = new FunctionTool({
  name: "get_current_time",
  description: "Get the current time.",
  parameters: {
    type: "object",
    properties: {},
  },
  execute: () => {
    return new Date().toLocaleTimeString("en-US", {
      hour: "numeric",
      minute: "2-digit",
      hour12: true,
    });
  },
});

// Create the agent
const agent = new LlmAgent({
  name: "weather_time_assistant",
  model: "gemini-2.5-flash",
  instruction: "You are a helpful assistant that can check weather and time.",
  tools: [getWeatherTool, getCurrentTimeTool],
});

// Create the runner and a session
const runner = new InMemoryRunner({ agent, appName: "weather_app" });
const userId = "user123";
const sessionId = "session123";
await runner.sessionService.createSession({
  appName: runner.appName,
  userId,
  sessionId,
});

// Run the agent
for await (const event of runner.runAsync({
  userId,
  sessionId,
  newMessage: {
    role: "user",
    parts: [{ text: "What's the weather like in New York?" }],
  },
})) {
  console.log(event);
}
```

## What Braintrust traces

Braintrust captures:

* Runner execution as a task span, with the new message as input, the final event as output, and the user and session IDs in metadata
* Agent invocations as task spans nested under the runner, with the agent name and model in metadata
* Tool calls as tool spans nested under the agent, with arguments as input, the result as output, and the tool name plus call ID in metadata
* Token usage (prompt, completion, total, cached, and reasoning tokens) aggregated across the run on the runner span
* LLM calls inside ADK as nested LLM spans, traced via Braintrust's Google GenAI integration since ADK uses Google GenAI internally for model calls

## Resources

* [Google ADK for TypeScript](https://github.com/google/adk-js)
* [Google Gemini models](https://ai.google.dev/)

**Python**

## Setup

Install the Braintrust and `google-adk` packages, then set your API keys. Requires `google-adk` v1.14.1 or later.

**1. Install packages**

```bash uv theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
uv add braintrust google-adk
```

```bash pip theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
pip install braintrust google-adk
```

**2. Set environment variables**

```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
BRAINTRUST_API_KEY=<your-braintrust-api-key>
GEMINI_API_KEY=<your-gemini-api-key>

# For organizations on the EU data plane, use https://api-eu.braintrust.dev
# For self-hosted deployments, use your data plane URL
# BRAINTRUST_API_URL=<your-braintrust-api-url>
```

## Auto-instrumentation

Call `setup_adk()` at startup to enable tracing for ADK agents.

```python Python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
import asyncio
import os

from braintrust.wrappers.adk import setup_adk
from google.adk import Runner
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService
from google.genai import types

# Call setup_adk() to enable automatic tracing for all ADK agent interactions
setup_adk(
    project_name="my-adk-project",
    api_key=os.environ.get("BRAINTRUST_API_KEY"),
)

# Create your ADK agent as normal
def get_weather(city: str) -> dict:
    """Get weather for a city."""
    return {"temperature": 72, "condition": "sunny", "city": city}

def get_current_time() -> str:
    """Get the current time."""
    from datetime import datetime

    return datetime.now().strftime("%I:%M %p")

async def main():
    # Create the agent
    agent = LlmAgent(
        name="weather_time_assistant",
        tools=[get_weather, get_current_time],
        model="gemini-2.5-flash",
        instruction="You are a helpful assistant that can check weather and time.",
    )
    # Create a session service and a runner
    session_service = InMemorySessionService()
    runner = Runner(app_name="weather_app", agent=agent, session_service=session_service)
    # Create a fake session
    user_id = "user123"
    session_id = "session123"
    await session_service.create_session(app_name="weather_app", user_id=user_id, session_id=session_id)
    # Create the message to send
    new_message = types.Content(
        parts=[types.Part(text="What's the weather like in New York?")],
        role="user",
    )
    # Run the agent with the query
    events = runner.run(
        user_id=user_id,
        session_id=session_id,
        new_message=new_message,
    )
    # Process the events and print the agent's response
    for event in events:
        print(event)

if __name__ == "__main__":
    asyncio.run(main())
```

## What Braintrust traces

Braintrust captures:

* Runner invocations as task spans named `invocation [<app_name>]`, with the new message as input, the final event as output, and the user and session IDs in metadata
* Agent runs as task spans named `agent_run [<agent_name>]`, nested under the runner
* Each model call cycle as a task span named `call_llm`
* Individual model invocations as task spans named `llm_call [<call_type>]`, where `<call_type>` is one of `direct_response`, `tool_selection`, or `response_generation`. The span captures the model request, the response, `time_to_first_token`, and the model, provider, and tool definitions in metadata
* Gemini requests as LLM spans named `generate_content`, nested under `llm_call [<call_type>]` and traced by Braintrust's [Google GenAI integration](https://docs.braintrust.dev/integrations/ai-providers/gemini.md), which `setup_adk()` enables. These spans carry the token usage (prompt, completion, total, cached, and reasoning tokens). For other model providers, enable that provider's integration to record token usage
* ADK 2.x workflow runs as task spans named `workflow [<workflow_name>]`, and each non-agent workflow node as a task span named `workflow_node [<node_name>]`, with the node input and output and the node name and class in metadata
* Tool calls as tool spans named `tool [<tool_name>]`, with the tool name and arguments as input and the result as output, including tools that a workflow node runs directly
* MCP tool calls as tool spans named `mcp_tool [<tool_name>]`

## Resources

* [Google ADK documentation for Python](https://github.com/google/adk-python)
* [Google Gemini models](https://ai.google.dev/)

**Go**

## Setup

Install the Braintrust Go SDK and the ADK contrib package, then set your API keys. Requires `google.golang.org/adk` v0.4.0 or later.

**1. Install packages**

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
go get github.com/braintrustdata/braintrust-sdk-go
go get github.com/braintrustdata/braintrust-sdk-go/trace/contrib/adk
```

**2. Set environment variables**

```bash title=".env" theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
BRAINTRUST_API_KEY=<your-braintrust-api-key>
GEMINI_API_KEY=<your-gemini-api-key>

# For organizations on the EU data plane, use https://api-eu.braintrust.dev
# For self-hosted deployments, use your data plane URL
# BRAINTRUST_API_URL=<your-braintrust-api-url>
```

## Manual instrumentation

Call `traceadk.AddLLMAgentCallbacks(&cfg)` on the agent config before constructing the agent.

```go Go theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
package main

import (
	"context"
	"fmt"
	"log"
	"os"
	"time"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/sdk/trace"
	"google.golang.org/adk/agent"
	"google.golang.org/adk/agent/llmagent"
	"google.golang.org/adk/model/gemini"
	"google.golang.org/adk/runner"
	"google.golang.org/adk/session"
	"google.golang.org/adk/tool"
	"google.golang.org/adk/tool/functiontool"
	"google.golang.org/genai"

	"github.com/braintrustdata/braintrust-sdk-go"
	traceadk "github.com/braintrustdata/braintrust-sdk-go/trace/contrib/adk"
)

type getWeatherArgs struct {
	City string `json:"city" jsonschema:"city to look up weather for"`
}

type getWeatherResult struct {
	Temperature int    `json:"temperature"`
	Condition   string `json:"condition"`
	City        string `json:"city"`
}

func getWeather(ctx tool.Context, args getWeatherArgs) (getWeatherResult, error) {
	return getWeatherResult{
		Temperature: 72,
		Condition:   "sunny",
		City:        args.City,
	}, nil
}

type getCurrentTimeArgs struct{}

type getCurrentTimeResult struct {
	Time string `json:"time"`
}

func getCurrentTime(ctx tool.Context, args getCurrentTimeArgs) (getCurrentTimeResult, error) {
	return getCurrentTimeResult{
		Time: time.Now().Format("3:04 PM"),
	}, nil
}

func main() {
	// Set up OpenTelemetry tracer
	tp := trace.NewTracerProvider()
	defer tp.Shutdown(context.Background())
	otel.SetTracerProvider(tp)

	// Initialize Braintrust tracing, WithBlockingLogin is just for local testing
	bt, err := braintrust.New(tp,
		braintrust.WithProject("my-adk-project"),
		braintrust.WithAPIKey(os.Getenv("BRAINTRUST_API_KEY")),
		braintrust.WithBlockingLogin(true),
	)
	if err != nil {
		log.Fatal(err)
	}

	ctx := context.Background()
	tracer := otel.Tracer("adk-example")
	ctx, span := tracer.Start(ctx, "adk-agent-run")
	defer span.End()

	// Set up ADK model
	model, err := gemini.NewModel(ctx, "gemini-2.5-flash", &genai.ClientConfig{
		APIKey: os.Getenv("GEMINI_API_KEY"),
	})
	if err != nil {
		log.Fatalf("Failed to create model: %v", err)
	}

	// Create the tools
	weatherTool, err := functiontool.New(
		functiontool.Config{
			Name:        "get_weather",
			Description: "Get weather for a city.",
		},
		getWeather,
	)
	if err != nil {
		log.Fatalf("Failed to create weather tool: %v", err)
	}

	currentTimeTool, err := functiontool.New(
		functiontool.Config{
			Name:        "get_current_time",
			Description: "Get the current time.",
		},
		getCurrentTime,
	)
	if err != nil {
		log.Fatalf("Failed to create time tool: %v", err)
	}

	// Create agent with tracing callbacks
	cfg := llmagent.Config{
		Name:        "weather_time_assistant",
		Model:       model,
		Description: "Weather and time helper",
		Instruction: "You are a helpful assistant that can check weather and time.",
		Tools:       []tool.Tool{weatherTool, currentTimeTool},
	}
	traceadk.AddLLMAgentCallbacks(&cfg)
	a, err := llmagent.New(cfg)
	if err != nil {
		log.Fatalf("Failed to create agent: %v", err)
	}

	// Create session and runner
	sessionSvc := session.InMemoryService()
	sessionSvc.Create(ctx, &session.CreateRequest{
		AppName:   "weather_app",
		UserID:    "user123",
		SessionID: "session123",
	})

	runner, err := runner.New(runner.Config{
		AppName:        "weather_app",
		Agent:          a,
		SessionService: sessionSvc,
	})
	if err != nil {
		log.Fatalf("Failed to create runner: %v", err)
	}

	// Run the agent
	msg := genai.NewContentFromText("What's the weather like in New York?", genai.RoleUser)
	for ev, err := range runner.Run(ctx, "user123", "session123", msg, agent.RunConfig{}) {
		if err != nil {
			log.Printf("Error: %v", err)
			return
		}
		if ev.Content != nil {
			for _, p := range ev.Content.Parts {
				if p.Text != "" {
					fmt.Printf("Response: %s\n", p.Text)
				}
			}
		}
	}

	fmt.Printf("View trace: %s\n", bt.Permalink(span))
}
```

## Avoiding duplicate spans

Google ADK has built-in OpenTelemetry support that emits spans under the `gcp.vertex.agent` instrumentation scope. Braintrust drops these by default to avoid duplicating the spans produced by Braintrust's ADK callbacks (found in `trace/contrib/adk`).

If you're not using `traceadk.AddLLMAgentCallbacks` (from the Braintrust ADK package), and you're instead relying solely on ADK's native telemetry, opt in to receive those spans by passing an option to `braintrust.New`:

```go #skip-compile theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
braintrust.New(tp, braintrust.EnableBuiltinAdkTraces())
```

Or by setting an environment variable:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
export BRAINTRUST_OTEL_ENABLE_BUILTIN_ADK_TRACES=true
```

## What Braintrust traces

Braintrust captures:

* Agent runs as task spans named `agent_run [<agent_name>]`, with the agent name, invocation ID, session ID, and branch in metadata. Sub-agent runs, including agent transfers and workflow agent children, are nested under the agent that called them when every agent in the tree uses the same callbacks, such as the default callbacks that `traceadk.AddLLMAgentCallbacks` and `traceadk.AddAgentCallbacks` install.
* LLM model calls as LLM spans named `call_llm`, with the request as input, the response as output, the model name and finish reason in metadata, and token usage (prompt, completion, total, reasoning, and cached tokens)
* Spans from model provider instrumentation, such as `generate_content` spans from Braintrust's [Google GenAI integration](https://docs.braintrust.dev/integrations/ai-providers/gemini.md#what-traced-go), nested under the `call_llm` span for that model call (including streaming calls), with provider errors that ADK doesn't recover from also recorded on the `call_llm` span. Requires Go SDK v0.17.2 or later, with `traceadk.AddLLMAgentCallbacks` using its default callbacks or callbacks from `traceadk.NewCallbacks`.
* Tool calls as tool spans named `tool [<tool_name>]`, with the tool name and description, arguments as input, and the result as output

## Resources

* [Google ADK documentation for Go](https://github.com/google/adk-go)
* [Google Gemini models](https://ai.google.dev/)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.