> ## 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 |

## Tool catalog capture (`ai.prompt.tools`)

`raindrop-google-adk` ≥ 0.0.17 registers an ADK plugin on the wrapped `Runner` whose `before_model_callback` reads `LlmRequest.config.tools`, the finished request ADK hands to the model, and binds it for that model call. Every model span under `Runner.run()` / `Runner.run_async()` therefore records the exact list: each `function_declarations` entry as a function tool (JSON Schema recorded as given) and built-ins such as `google_search`, `code_execution`, `retrieval` or `url_context` as provider-defined tools without an invented schema. The OpenLLMetry Google GenAI / Vertex AI instrumentations record no tool definitions themselves.

Pass `tools=` on `RaindropGoogleADK(...)`, `setup_google_adk(...)` or `create_raindrop_google_adk(...)` to record a different list for every model span of that wrapper's runs (it replaces the inferred one; `tools=[]` records an empty catalog). `Runner.run()` takes no per-call metadata, so there is no per-call override, and `setup()`'s class patch is keep-first: a second `setup()` with a different `tools` warns and keeps the first. `Runner.run_live()` runs no `before_model_callback`, so live runs record only an explicit override (held for the whole stream); without one the attribute stays absent. Follows `TRACELOOP_TRACE_CONTENT`; needs `raindrop-ai` ≥ 0.0.70. See [Tool catalog capture](/docs/sdk/tool-catalog) for the attribute format, the absent / `[]` semantics and the override shapes accepted.

## 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
```

## Application Git metadata (Python)

`RaindropGoogleADK(...)`, `create_raindrop_google_adk(...)`, and `setup_google_adk(...)` accept the keyword-only `app_git` option. It defaults to `True`: explicit Raindrop Git environment or deployment context is applied immediately, and the base SDK may perform one bounded background local-Git lookup from the process working directory. Event capture, flush, and shutdown never wait for that lookup. Pass `False` to disable enrichment, or pass an `AppGitOptions` mapping with `commit_sha`, `commit_dirty`, `branch`, `source_directory`, `detect_branch`, and/or `auto_detect`. Automatic branch discovery remains opt-in through `detect_branch=True` (or `RAINDROP_GIT_DETECT_BRANCH=true`).

For an ordinary in-process application, the process working directory is treated as the application-under-test checkout. A remote, coding, workflow, or observer process must not rely on its own checkout: pass `app_git=False`, provide explicit revision values, or set `source_directory` to the actual application checkout. Canonical per-operation properties remain authoritative. When supplying `client=`, configure `app_git` while constructing that `Raindrop` client; the supplied client is authoritative and the wrapper's `app_git` argument does not reconfigure it.

Release order is deliberate: first publish the base SDK feature, then publish the wrapper feature release with its minimum dependency coordinated to that base release. The existing `raindrop-ai` lower bound remains compatible, but application Git metadata is unavailable on an older core and must not be claimed complete until the base is upgraded. Until coordination assigns a released version, the wrapper checks for an explicit base `app_git` parameter and omits the option when unsupported. Explicit non-default configuration is debug-logged and omitted. Unsupported `app_git` is determined by signature inspection before construction, not by retrying initialization after a `TypeError`; Git configuration adds no initialization attempts and does not change any existing framework-specific initialization fallback.

## 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>


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