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

# Google ADK Integration

> Register one Plugin on your Google ADK Runner to automatically mirror every run into a Sentrial session — model calls, tool calls, tokens, cost, and errors.

> [!NOTE]
>
> **Python** (`pip install 'sentrial[google-adk]'`) **and TypeScript** (`@sentrial/sdk`, with `@google/adk`). Sentrial attaches to Google's [Agent Development Kit](https://google.github.io/adk-docs/) as a Runner-level **Plugin** (the recommended surface); Python also offers a per-agent **callbacks** helper. ADK is an optional, duck-typed peer dependency in both SDKs — neither ever imports it, so the SDK loads with or without ADK installed.

**Python**

Read on — the sections below cover the Python Plugin, the per-agent callbacks helper, and the full mapping.

**TypeScript**

Jump to [TypeScript (adk-js)](#typescript-adk-js) for the `@sentrial/sdk` twin. The run→session mapping is identical to Python's.

## What Gets Tracked Automatically

> **Sessions**
>
> One session per ADK run (invocation) — agent name from the root agent, aggregated tokens and cost, final output.

> **Model Calls**
>
> Every model call as a per-call event — model, prompt/completion tokens from `usage_metadata`, auto-calculated cost, duration.

> **Tool Calls**
>
> Each tool execution with the tool name, arguments, result, and errors.

> **Errors**
>
> `on_tool_error` / `on_model_error` and any `LlmResponse.error_code` become errors and fail the session.

> **Conversation Linkage**
>
> The ADK session id becomes the Sentrial `convoId`, threading multi-turn runs together.

> **Fail-safe**
>
> Every callback is containment-wrapped — a Sentrial error is logged and swallowed, never crashing your agent.

## Installation

```bash theme={null}
pip install 'sentrial[google-adk]'
# The extra installs google-adk>=1.5.0,<3.0.0 — the tested/supported window.
# The Plugin surface needs ADK's plugins system (>=1.5.0); the per-agent
# callbacks helper works on all ADK 1.x.
```

## Quick Start (Plugin — recommended)

Register one Plugin on your `Runner`. Then run your agents exactly like before — the Plugin fires globally across every agent, tool, and model call in the invocation.

```python theme={null}
import sentrial
from google.adk.agents import LlmAgent
from google.adk.runners import InMemoryRunner
from sentrial.google_adk import SentrialAdkPlugin

sentrial.configure(api_key="sentrial_live_xxx")

agent = LlmAgent(name="assistant", model="gemini-2.0-flash")
runner = InMemoryRunner(agent=agent, plugins=[SentrialAdkPlugin()])
# Every runner.run_async(...) is now a Sentrial session with model events,
# tool calls, and token/cost rollups.
```

Already constructed the `Runner`? Attach after the fact with `instrument_adk`:

```python theme={null}
from google.adk.runners import InMemoryRunner
from sentrial.google_adk import instrument_adk

runner = InMemoryRunner(agent=agent)
instrument_adk(runner, user_id="user_123")
```

## Per-Agent Callbacks (alternative)

When you can't register a Runner-level Plugin, attach the callbacks to a single agent. This only observes the agent(s) you attach it to — sub-agents need their own attachment — so prefer the Plugin for full coverage.

```python theme={null}
from google.adk.agents import LlmAgent
from sentrial.google_adk import sentrial_adk_callbacks

agent = LlmAgent(
    name="assistant",
    model="gemini-2.0-flash",
    **sentrial_adk_callbacks(user_id="user_123"),
)
```

`sentrial_adk_callbacks()` returns the `before_/after_agent`, `before_/after_model`, and `before_/after_tool` callback dict; the session is bracketed by the agent's `before_agent` / `after_agent`.

## Configuration Options

Both `SentrialAdkPlugin(...)`, `instrument_adk(runner, ...)`, and `sentrial_adk_callbacks(...)` take the same options:

```python theme={null}
SentrialAdkPlugin(
    client=my_client,            # optional — explicit SentrialClient;
                                 # defaults to the module-level client from sentrial.configure()
    default_agent="my-agent",    # optional — explicit agent/event name
                                 # (defaults to the root agent's name per run)
    user_id="user_123",          # optional — defaults to the ADK session's user_id,
                                 # then "anonymous"
    extra_metadata={"env": "prod"},  # optional — merged into every session
)
```

> [!TIP]
>
> **Idempotent registration** — `instrument_adk()` is idempotent per runner: calling it again on the same runner returns the first plugin instead of registering a duplicate (which would mirror every run into two sessions). ADK also dedups plugins by `name`, so passing two identically-named plugins is rejected by ADK itself.

## How Runs Map to Sessions

| Google ADK | Sentrial |
| - | - |
| Run / invocation (`before_run` → `after_run`) | Session — agent/event name from the root agent, `convoId` from the ADK session id, user from the ADK session `user_id`, input from the run's `user_content` |
| Model calls (`before_model_callback` → `after_model_callback`) | Per-call LLM events (`llm:{model}`) with model from `LlmRequest.model`, prompt/completion tokens from `LlmResponse.usage_metadata` (`prompt_token_count` / `candidates_token_count`), auto-calculated cost, and duration from the before/after timestamps |
| Tool calls (`before_tool_callback` → `after_tool_callback`) | Tool calls with name, arguments, result, and duration |
| `on_tool_error` / `on_model_error` / `LlmResponse.error_code` | Errors — the session finishes `success=False` with the failure reason |
| Run end (`after_run_callback`) | `finish()` — aggregated tokens/cost, last model output as the session output, `llm_calls` / `tool_calls` custom metrics |

Additional mapping notes:

* Session metadata carries `integration: "google-adk"` plus the ADK `invocation_id` and `app_name`.
* **Streaming**: partial model responses are skipped so tokens aren't double-counted — the final response carries `usage_metadata` and is the one counted.
* Model duration is measured from `before_model` → `after_model`; tool duration from `before_tool` → `after_tool` (correlated by the `ToolContext` instance, so parallel tools are timed correctly).

## Don't Combine with `wrap_google`

> [!WARNING]
>
> `wrap_google()` wraps a **raw** `google.generativeai.GenerativeModel` so each `generate_content` call is tracked inside a session you manage with `begin()`. This integration instead observes the ADK **runtime** (`Agent` + `Runner`) from the outside. If you use ADK, prefer this integration; do **not** combine both on the same run (LLM calls would be double-counted), and do **not** attach both the Plugin and the callbacks helper.

## Batching & Short-Lived Processes

ADK invokes Plugin/callbacks inline on the run's event loop, so an explicit client with **batching enabled** is recommended — each `track` call then enqueues and returns immediately instead of waiting on a network round-trip per event:

```python theme={null}
from sentrial import SentrialClient
from sentrial.google_adk import SentrialAdkPlugin

client = SentrialClient(api_key="sentrial_live_xxx", batching=True)
runner = InMemoryRunner(agent=agent, plugins=[SentrialAdkPlugin(client)])
```

In short-lived processes (scripts, serverless functions), don't exit before queued events are sent — call `client.flush()` after your last run when batching is enabled.

## TypeScript (adk-js)

The TypeScript SDK ships the same integration for [`adk-js`](https://github.com/google/adk-js) (`@google/adk`). It attaches as a Runner-level Plugin — `@google/adk` is an optional, duck-typed peer dependency the SDK never imports, so `@sentrial/sdk` loads with or without ADK installed. The run→session mapping is identical to Python's.

> [!NOTE]
>
> **Callback naming.** ADK-JS callbacks are camelCase and take a single params object (e.g. `afterModelCallback({ callbackContext, llmResponse })`) — unlike Python's snake\_case keyword args. The mapping is otherwise the same. There is no per-agent callbacks helper on the TS side (yet); use the Runner-level Plugin.

### Installation

```bash theme={null}
npm install @sentrial/sdk @google/adk
```

### Quick Start (Plugin — recommended)

```typescript theme={null}
import { InMemoryRunner, LlmAgent } from '@google/adk';
import { sentrial, SentrialAdkPlugin } from '@sentrial/sdk';

sentrial.configure({ apiKey: process.env.SENTRIAL_API_KEY });

const agent = new LlmAgent({ name: 'assistant', model: 'gemini-2.0-flash' });
const runner = new InMemoryRunner({ agent, plugins: [new SentrialAdkPlugin()] });
// Every runner.runAsync(...) is now a Sentrial session with model events,
// tool calls, and token/cost rollups.
```

Already constructed the `Runner`? Attach after the fact with `instrumentAdk` (idempotent per runner):

```typescript theme={null}
import { instrumentAdk } from '@sentrial/sdk';

instrumentAdk(runner, { userId: 'user_123' });
```

### Configuration Options

`new SentrialAdkPlugin(options)` and `instrumentAdk(runner, options)` take the same options:

```typescript theme={null}
new SentrialAdkPlugin({
  client: myClient, // optional — explicit SentrialClient; defaults to an env client (SENTRIAL_API_KEY)
  name: 'sentrial', // optional — ADK plugin name (must be unique per runner)
  defaultAgent: 'my-agent', // optional — explicit agent/event name (defaults to the root agent's name)
  userId: 'user_123', // optional — defaults to the ADK session's userId, then 'anonymous'
  extraMetadata: { env: 'prod' }, // optional — merged into every session
});
```

> [!TIP]
>
> **Batching** — ADK invokes plugin callbacks inline on the run's event loop, so an explicit client with batching enabled is recommended: `new SentrialClient({ apiKey, batching: { enabled: true } })`. Each `track` call then enqueues and returns immediately. In short-lived processes, call `client.flush()` after your last run.

> [!WARNING]
>
> Don't combine with `wrapGoogle()` on the same run. `wrapGoogle()` wraps a **raw** `@google/genai` model so each `generateContent` call is tracked inside a session you manage with `begin()`; this integration observes the ADK **runtime** from the outside. Pick one, or LLM calls are double-counted.

## Next Steps

> **[Python SDK Reference](https://www.sentrial.com/docs/sdk/python.md)**
>
> Full SDK documentation with all methods and options.

> **[OpenAI Agents SDK](https://www.sentrial.com/docs/integrations/openai-agents.md)**
>
> The same processor-style integration for the OpenAI Agents SDK.


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