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

# Google ADK

> Automatic tracing for Google ADK agents, tools, and multi-step reasoning with Raindrop

## Installation

```bash theme={null}
pip install raindrop-google-adk google-adk
```

## Quick Start

```python theme={null}
from raindrop_google_adk import setup_google_adk
from google.adk import Runner
from google.adk.agents import LlmAgent
from google.adk.sessions import InMemorySessionService
from google.genai import types

# Enable automatic tracing for all ADK Runner interactions
rd = setup_google_adk(api_key="your-write-key")

def get_weather(city: str) -> dict:
    """Get weather for a city."""
    return {"temperature": 72, "condition": "sunny", "city": city}

agent = LlmAgent(
    name="weather_assistant",
    tools=[get_weather],
    model="gemini-2.5-flash",
    instruction="You are a helpful assistant that can check weather.",
)

session_service = InMemorySessionService()
runner = Runner(app_name="weather_app", agent=agent, session_service=session_service)

user_msg = types.Content(
    parts=[types.Part(text="What's the weather in New York?")],
    role="user",
)

for event in runner.run(user_id="user123", session_id="session123", new_message=user_msg):
    if event.is_final_response():
        print(event.content.parts[0].text)

```

## What Gets Traced

The Google ADK integration automatically captures:

* **Runner invocations** — input message, user\_id, session\_id, app\_name
* **Agent responses** — final output text from the agent
* **Token usage** — prompt\_tokens, completion\_tokens, total\_tokens from usage metadata
* **Tool calls** — individual tool spans with name, input, output, duration, and error
* **Model info** — model version when available (e.g., `gemini-2.5-flash`)
* **Agent identity** — agent name, author from events
* **Finish reason** — why generation stopped (e.g., STOP, SAFETY, MAX\_TOKENS)
* **Errors** — captured (with error message) and re-raised to the caller
* **Async support** — both `run()` (sync) and `run_async()` (async) are instrumented

## Configuration

### Auto-instrumentation (recommended)

Use `setup_google_adk()` to automatically patch all ADK Runner instances:

```python theme={null}
from raindrop_google_adk import setup_google_adk

rd = setup_google_adk(
    api_key="your-write-key",      # Required: your Raindrop API key. If None, telemetry is disabled.
    user_id="user-123",            # Optional: default user ID for all events
    convo_id="convo-456",          # Optional: conversation/thread ID
    project_id="support-prod",     # Optional: route events to a specific project (slug)
    tracing_enabled=True,          # Optional: enable/disable OTEL tracing (default: True)
    bypass_otel_for_tools=True,    # Optional: bypass OTEL for tool calls (default: True)
)

# All Runner.run() and Runner.run_async() calls are now automatically traced
# Call rd.shutdown() before process exit
```

### Manual wrapping

Use `create_raindrop_google_adk()` to wrap specific Runner instances:

```python theme={null}
from raindrop_google_adk import create_raindrop_google_adk

rd = create_raindrop_google_adk(
    api_key="your-write-key",
    user_id="user-123",
)

wrapped_runner = rd.wrap(runner)

for event in wrapped_runner.run(user_id="user123", session_id="s1", new_message=msg):
    print(event)

rd.shutdown()
```

### Class-based API

```python theme={null}
from raindrop_google_adk import RaindropGoogleADK

rd = RaindropGoogleADK(
    api_key="your-write-key",
    user_id="user-123",
    tracing_enabled=True,
    bypass_otel_for_tools=True,
    debug=False,
)

# Auto-patch all Runner instances
rd.setup()

# Or wrap a specific runner
wrapped = rd.wrap(runner)

# Cleanup
rd.shutdown()
```

### Debug Mode

Enable verbose logging to troubleshoot integration issues:

```python theme={null}
rd = RaindropGoogleADK(
    api_key="your-write-key",
    debug=True,  # Enables DEBUG-level logging for the integration
)
```

When `debug=True`, internal telemetry operations (event extraction, interaction lifecycle) are logged at the DEBUG level via Python's standard `logging` module.

### Projects

Route events to a specific [project](/docs/platform/projects) by passing its slug as `project_id`:

```python theme={null}
rd = setup_google_adk(
    api_key="your-write-key",
    project_id="support-prod",
)
```

`project_id` sets the `X-Raindrop-Project-Id` header on every event. Omit it (or pass `"default"`) to use your org's default **Production** project, which is the existing behavior. The same option is accepted by `create_raindrop_google_adk(...)` and `RaindropGoogleADK(...)`. Invalid slugs are ignored with a warning and no header is sent.

### Multiple projects in one process

Available in `raindrop-ai>=0.0.56`. When one service runs several ADK runners
that should report to **different** projects, create one `RaindropGoogleADK`
wrapper per project. Each wrapper owns its own `raindrop.Raindrop` client, so
the two route independently — there is no shared module-level state:

```python theme={null}
from raindrop_google_adk import RaindropGoogleADK

# One long-lived wrapper per project, created at startup and reused.
rd_support = RaindropGoogleADK(api_key="your-write-key", project_id="support-prod")
rd_billing = RaindropGoogleADK(api_key="your-write-key", project_id="billing-prod")

# Wrap each runner with the corresponding wrapper.
support_runner = rd_support.wrap(support_runner)   # -> support-prod
billing_runner = rd_billing.wrap(billing_runner)   # -> billing-prod

rd_support.flush()
rd_billing.flush()
```

Each wrapper owns its configuration and delivery pipeline, so runners handled by
different wrappers route independently. To share a single client across wrappers
(or with the module-level API), construct a `raindrop.Raindrop` yourself and pass
it via `client=`:

```python theme={null}
from raindrop import Raindrop

client = Raindrop(api_key="your-write-key", project_id="support-prod")
rd = RaindropGoogleADK(client=client)
```

<Note>
  Use `wrap()` — not `setup()` — for multiple projects. `setup_google_adk()` /
  `RaindropGoogleADK.setup()` patches the ADK `Runner` class **process-globally**,
  so only the first `setup()`'s project owns class-wide auto-instrumentation; a
  second `setup()` with a different project keeps routing to the first and emits a
  warning. `wrap(runner)` instruments a specific runner with that wrapper's own
  client, taking precedence over any class-level `setup()` patch for that runner —
  so per-runner routing stays correct even if `setup()` ran earlier in the process.
</Note>

## Identify

Associate a user with traits for downstream analysis:

```python theme={null}
rd.identify(
    user_id="user-123",
    traits={"plan": "pro", "company": "Acme"},
)
```

## Track Signal

Track feedback, edits, or custom signals tied to a specific event:

```python theme={null}
rd.track_signal(
    event_id="evt-abc123",
    name="thumbs_up",
    signal_type="feedback",
    sentiment="POSITIVE",
    comment="Great response!",
)
```

## Tool Call Tracking

When your agent uses tools, individual tool spans are captured automatically with name, input, output, duration, and error status:

```python theme={null}
def get_stock_price(symbol: str) -> str:
    """Get the current stock price."""
    return "189.50"

agent = LlmAgent(
    name="stock_agent",
    tools=[get_stock_price],
    model="gemini-2.5-flash",
    instruction="Answer questions about stock prices.",
)

# Tool calls are tracked as individual spans and counted in event properties as
# google_adk.tool_calls_count and google_adk.tool_call_names
```

## Multi-Agent Workflows

Google ADK supports complex agent topologies — sequential agents, parallel agents,
and nested sub-agents. The integration captures the top-level Runner invocations
regardless of agent complexity:

```python theme={null}
from google.adk.agents import LlmAgent, SequentialAgent, ParallelAgent

researcher = LlmAgent(name="researcher", model="gemini-2.5-flash",
    instruction="Research the topic.", tools=[search_tool])
writer = LlmAgent(name="writer", model="gemini-2.5-flash",
    instruction="Write a summary based on research.")

pipeline = SequentialAgent(name="research_pipeline", sub_agents=[researcher, writer])

runner = Runner(app_name="research_app", agent=pipeline, session_service=session_service)
# Runner invocations are automatically traced with input/output capture
```

The integration records one `ai_generation` event per `Runner.run()` invocation.
ADK can mark one final response per participating agent, so for multi-agent
invocations the event output is the last non-empty final agent response. Earlier
agent responses are not merged into the displayed conversation output.
Output-specific metadata such as model, author, agent name, branch, and finish
reason comes from that selected response, while token and tool counts cover the
full invocation.

## Async Usage

The wrapper supports both sync and async runner usage:

```python theme={null}
import asyncio

async def main():
    user_msg = types.Content(
        parts=[types.Part(text="What's the weather?")],
        role="user",
    )
    async for event in runner.run_async(
        user_id="user123",
        session_id="session123",
        new_message=user_msg,
    ):
        if event.is_final_response():
            print(event.content.parts[0].text)

    rd.shutdown()

asyncio.run(main())
```

## Finish Reason Tracking

The integration captures the `finish_reason` from model responses, indicating why generation stopped. Common values include:

| Value        | Meaning                   |
| ------------ | ------------------------- |
| `STOP`       | Normal completion         |
| `SAFETY`     | Blocked by safety filters |
| `MAX_TOKENS` | Hit token limit           |
| `RECITATION` | Blocked for recitation    |

This is available in event properties as `google_adk.finish_reason`.

## Token Tracking

Token usage is accumulated across all model calls within a single `Runner.run()` invocation. The following fields are captured:

| Property                     | Description                                |
| ---------------------------- | ------------------------------------------ |
| `ai.usage.prompt_tokens`     | Total input tokens across all model calls  |
| `ai.usage.completion_tokens` | Total output tokens across all model calls |
| `ai.usage.total_tokens`      | Total tokens (prompt + completion)         |
| `ai.usage.cached_tokens`     | Cached content token count                 |
| `ai.usage.thoughts_tokens`   | Thinking/reasoning token count             |

## Captured Properties

Each event includes the following properties when available:

| Property                       | Description                                 |
| ------------------------------ | ------------------------------------------- |
| `ai.usage.prompt_tokens`       | Input token count                           |
| `ai.usage.completion_tokens`   | Output token count                          |
| `ai.usage.total_tokens`        | Total token count                           |
| `ai.usage.cached_tokens`       | Cached content token count                  |
| `ai.usage.thoughts_tokens`     | Thinking/reasoning token count              |
| `google_adk.app_name`          | Runner app name                             |
| `google_adk.user_id`           | User ID from the runner call                |
| `google_adk.session_id`        | Session ID from the runner call             |
| `google_adk.author`            | Event author (agent name)                   |
| `google_adk.agent_name`        | Agent name from the event                   |
| `google_adk.tool_calls_count`  | Number of tool calls in the run             |
| `google_adk.tool_call_names`   | JSON list of tool names called              |
| `google_adk.finish_reason`     | Why generation stopped (STOP, SAFETY, etc.) |
| `google_adk.error`             | Whether a Python exception occurred         |
| `google_adk.error_message`     | Python exception message if applicable      |
| `google_adk.error_code`        | LLM-level error code (e.g. SAFETY, QUOTA)   |
| `google_adk.llm_error_message` | LLM-level error message                     |
| `google_adk.agent_branch`      | Agent hierarchy path for multi-agent setups |

## Flushing and Shutdown

Always call `shutdown()` before your process exits to ensure all telemetry is shipped:

```python theme={null}
rd.flush()     # flush pending events without releasing resources
rd.shutdown()  # flush + release resources
```

## Factory Function (backward compat)

The `setup_google_adk()` and `create_raindrop_google_adk()` factory functions return `RaindropGoogleADK` instances and accept the same parameters:

```python theme={null}
from raindrop_google_adk import setup_google_adk, create_raindrop_google_adk

# Auto-patch all Runner instances
rd = setup_google_adk(api_key="your-write-key")

# Or create without auto-patching
rd = create_raindrop_google_adk(api_key="your-write-key")
wrapped = rd.wrap(runner)
```

<Tip>
  The `RaindropGoogleADK` class provides `identify()` and `track_signal()` methods directly, so you don't need to import the core SDK separately.
</Tip>
