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

# Google ADK (Native)

> Learn how to use the native galileo-adk package for automatic tracing of Google ADK agents

The `galileo-adk` package provides native observability for [Google ADK](https://github.com/google/adk-python) agents. It automatically traces agent runs, LLM calls, and tool executions with minimal setup.

<Info>
  This is the native integration for Google ADK. If you prefer using OpenTelemetry, see the [OpenTelemetry-based integration](/sdk-api/third-party-integrations/opentelemetry-and-openinference/google-adk).
</Info>

## Installation

```bash theme={null}
pip install galileo-adk
```

<Note>
  View the package on [PyPI](https://pypi.org/project/galileo-adk/) for version history and additional details.
</Note>

**Requirements:** Python 3.10+, a [Galileo API key](https://app.galileo.ai/settings/api-keys), and a [Google AI API key](https://aistudio.google.com/apikey)

## Quick start

The simplest way to add Galileo observability to your Google ADK application is using the `GalileoADKPlugin`. This plugin attaches to the ADK Runner and automatically captures all agent, LLM, and tool events.

<CodeGroup>
  ```python Python theme={null}
  import asyncio
  from galileo_adk import GalileoADKPlugin
  from google.adk.runners import Runner
  from google.adk.agents import LlmAgent
  from google.genai import types

  async def main():
      # Create the Galileo ADK plugin
      plugin = GalileoADKPlugin(project="my-project", log_stream="production")

      # Create your agent
      agent = LlmAgent(
          name="assistant",
          model="gemini-2.0-flash",
          instruction="You are helpful."
      )

      # Pass the plugin to the Runner
      runner = Runner(agent=agent, plugins=[plugin])

      # Run the agent
      message = types.Content(
          parts=[types.Part(text="Hello! What can you help me with?")]
      )
      async for event in runner.run_async(
          user_id="user-123",
          session_id="session-456",
          new_message=message
      ):
          if event.is_final_response():
              print(event.content.parts[0].text)

  if __name__ == "__main__":
      # Set environment variables: GALILEO_API_KEY, GOOGLE_API_KEY
      asyncio.run(main())
  ```
</CodeGroup>

Once the plugin is configured, all agent runs are automatically logged to Galileo, including:

* Agent execution spans
* LLM calls with input/output and token usage
* Tool executions with arguments and results
* Error handling and status codes

## Configuration

The plugin can be configured via constructor parameters or environment variables:

| Parameter        | Environment Variable | Description                                                 |
| ---------------- | -------------------- | ----------------------------------------------------------- |
| `project`        | `GALILEO_PROJECT`    | Project name (required unless `ingestion_hook` provided)    |
| `log_stream`     | `GALILEO_LOG_STREAM` | Log stream name (required unless `ingestion_hook` provided) |
| `ingestion_hook` | -                    | Custom callback for trace data (bypasses Galileo backend)   |

## Features

### Session tracking

All traces with the same ADK `session_id` are automatically grouped into a Galileo session. This enables conversation-level tracking across multiple interactions:

<CodeGroup>
  ```python Python theme={null}
  import asyncio
  from galileo_adk import GalileoADKPlugin
  from google.adk.runners import Runner
  from google.adk.agents import LlmAgent
  from google.genai import types

  async def main():
      plugin = GalileoADKPlugin(project="my-project", log_stream="production")
      agent = LlmAgent(
          name="assistant",
          model="gemini-2.0-flash",
          instruction="You are helpful."
      )
      runner = Runner(agent=agent, plugins=[plugin])

      # All traces in this conversation are grouped together
      session_id = "conversation-abc"

      # First message
      message1 = types.Content(
          parts=[types.Part(text="Hello! What's the capital of France?")]
      )
      async for event in runner.run_async(
          user_id="user-123",
          session_id=session_id,
          new_message=message1
      ):
          if event.is_final_response():
              print(f"Response 1: {event.content.parts[0].text}")

      # Follow-up in same session
      message2 = types.Content(
          parts=[types.Part(text="What about Germany?")]
      )
      async for event in runner.run_async(
          user_id="user-123",
          session_id=session_id,
          new_message=message2
      ):
          if event.is_final_response():
              print(f"Response 2: {event.content.parts[0].text}")

  if __name__ == "__main__":
      # Set environment variables: GALILEO_API_KEY, GOOGLE_API_KEY
      asyncio.run(main())
  ```
</CodeGroup>

### Custom metadata

Attach custom metadata to traces using ADK's native `RunConfig.custom_metadata`. Metadata is propagated to all spans (agent, LLM, tool) within the invocation:

<CodeGroup>
  ```python Python theme={null}
  import asyncio
  from galileo_adk import GalileoADKPlugin
  from google.adk.runners import Runner
  from google.adk.agents import LlmAgent
  from google.adk.agents.run_config import RunConfig
  from google.genai import types

  async def main():
      plugin = GalileoADKPlugin(project="my-project", log_stream="production")
      agent = LlmAgent(
          name="assistant",
          model="gemini-2.0-flash",
          instruction="You are helpful."
      )
      runner = Runner(agent=agent, plugins=[plugin])

      # Attach custom metadata to the trace
      run_config = RunConfig(
          custom_metadata={
              "user_tier": "premium",
              "conversation_id": "conv-abc",
              "turn": 1,
              "experiment_group": "A",
          }
      )

      message = types.Content(
          parts=[types.Part(text="Hello! Tell me a fun fact.")]
      )
      async for event in runner.run_async(
          user_id="user-123",
          session_id="session-456",
          new_message=message,
          run_config=run_config,  # Pass the run config with metadata
      ):
          if event.is_final_response():
              print(event.content.parts[0].text)

  if __name__ == "__main__":
      # Set environment variables: GALILEO_API_KEY, GOOGLE_API_KEY
      asyncio.run(main())
  ```
</CodeGroup>

### Callback mode

For granular control over which callbacks to use, you can attach them directly to your agent instead of using the plugin. This is useful when you need to customize behavior for specific agents:

<CodeGroup>
  ```python Python theme={null}
  import asyncio
  from galileo_adk import GalileoADKCallback
  from google.adk.runners import Runner
  from google.adk.agents import LlmAgent
  from google.genai import types

  async def main():
      # Create the Galileo ADK callback
      callback = GalileoADKCallback(project="my-project", log_stream="production")

      # Attach callbacks directly to your agent
      agent = LlmAgent(
          name="assistant",
          model="gemini-2.0-flash",
          instruction="You are helpful.",
          before_agent_callback=callback.before_agent_callback,
          after_agent_callback=callback.after_agent_callback,
          before_model_callback=callback.before_model_callback,
          after_model_callback=callback.after_model_callback,
          before_tool_callback=callback.before_tool_callback,
          after_tool_callback=callback.after_tool_callback,
      )

      # Create the runner without the plugin
      runner = Runner(agent=agent)

      message = types.Content(
          parts=[types.Part(text="Hello! How are you?")]
      )
      async for event in runner.run_async(
          user_id="user-123",
          session_id="session-456",
          new_message=message
      ):
          if event.is_final_response():
              print(event.content.parts[0].text)

  if __name__ == "__main__":
      # Set environment variables: GALILEO_API_KEY, GOOGLE_API_KEY
      asyncio.run(main())
  ```
</CodeGroup>

### Retriever spans

By default, all `FunctionTool` calls are logged as tool spans. To log a retriever function as a **retriever span** (enabling RAG quality metrics in Galileo), decorate it with `@galileo_retriever`:

<CodeGroup>
  ```python Python theme={null}
  from galileo_adk import galileo_retriever
  from google.adk.tools import FunctionTool

  @galileo_retriever
  def search_docs(query: str) -> str:
      """Search the knowledge base."""
      # Your retrieval logic here
      results = my_vector_db.search(query)
      return "\n".join(r["content"] for r in results)

  # Wrap in a FunctionTool - Galileo will log this as a retriever span
  tool = FunctionTool(search_docs)
  ```
</CodeGroup>

This enables RAG-specific metrics like chunk attribution and context relevance to be calculated for your retrieval operations.

### Custom ingestion hook

For advanced use cases, you can intercept traces for custom processing before forwarding to Galileo. This is useful for:

* Custom session management
* Local trace inspection and debugging
* Filtering or enriching trace data

<CodeGroup>
  ```python Python theme={null}
  import asyncio
  import os
  from galileo import GalileoLogger
  from galileo_adk import GalileoADKPlugin
  from google.adk.runners import Runner
  from google.adk.agents import LlmAgent
  from google.genai import types

  # Create a Galileo logger for custom trace handling
  logger = GalileoLogger(
      project=os.getenv("GALILEO_PROJECT", "my-project"),
      log_stream=os.getenv("GALILEO_LOG_STREAM", "dev"),
  )

  def my_ingestion_hook(request):
      """Hook that captures traces locally and forwards to Galileo."""
      if hasattr(request, "traces") and request.traces:
          print(f"\n[Ingestion Hook] Intercepted {len(request.traces)} trace(s)")
          for trace in request.traces:
              spans = getattr(trace, "spans", []) or []
              span_types = [getattr(s, "type", "unknown") for s in spans]
              print(f"  - Trace with {len(spans)} span(s): {span_types}")

      # Session management: same external_id returns the same Galileo session
      galileo_session_id = logger.start_session(external_id=request.session_external_id)
      request.session_id = galileo_session_id

      # Forward traces to Galileo
      logger.ingest_traces(request)

  async def main():
      # Create plugin with custom ingestion hook
      plugin = GalileoADKPlugin(ingestion_hook=my_ingestion_hook)

      agent = LlmAgent(
          name="assistant",
          model="gemini-2.0-flash",
          instruction="You are helpful."
      )
      runner = Runner(agent=agent, plugins=[plugin])

      message = types.Content(parts=[types.Part(text="Hello!")])
      async for event in runner.run_async(
          user_id="user-123",
          session_id="session-456",
          new_message=message
      ):
          if event.is_final_response():
              print(event.content.parts[0].text)

  if __name__ == "__main__":
      # Set environment variables: GALILEO_API_KEY, GOOGLE_API_KEY
      asyncio.run(main())
  ```
</CodeGroup>

## Plugin vs Callback mode

| Feature                  | GalileoADKPlugin | GalileoADKCallback |
| ------------------------ | ---------------- | ------------------ |
| Invocation tracking      | Yes              | No                 |
| Multi-agent support      | Full             | Limited            |
| Sub-invocation detection | Yes              | No                 |
| Setup complexity         | Lower            | Higher             |
| Granular control         | Lower            | Higher             |

**Recommendation:** Use `GalileoADKPlugin` for most applications. Use `GalileoADKCallback` only when you need fine-grained control over individual agent callbacks.

## Next steps

<CardGroup cols={2}>
  <Card title="Galileo logger" icon="code" horizontal href="/sdk-api/logging/galileo-logger">
    Log with full control over sessions, traces, and spans using the Galileo logger.
  </Card>

  <Card title="OpenTelemetry integration" icon="code" horizontal href="/sdk-api/third-party-integrations/opentelemetry-and-openinference/google-adk">
    Use OpenTelemetry for Google ADK observability.
  </Card>

  <Card title="RAG metrics" icon="chart-line" horizontal href="/concepts/metrics/rag/generation-quality/chunk-attribution">
    Learn about RAG quality metrics for retriever spans.
  </Card>

  <Card title="Sessions" icon="messages" horizontal href="/concepts/logging/sessions/sessions-overview">
    Learn how sessions group related traces together.
  </Card>
</CardGroup>
