Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 113 additions & 0 deletions contributing/samples/integrations/snowflake_cortex_agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# Snowflake Cortex Analyst Agent

## Overview

This sample runs an existing [Snowflake Cortex Agent](https://docs.snowflake.com/en/user-guide/snowflake-cortex/cortex-agents)
as a native ADK root agent using `SnowflakeCortexAgent`. Snowflake runs the
agent loop and its server-side tools (Cortex Analyst, Cortex Search, SQL
execution); each ADK turn is sent to the Cortex Agents Run API and the run comes
back as standard ADK events: streamed text, the tool calls Snowflake made, and
one final answer whose citations, warnings, tables and charts are recorded as
event metadata. The Snowflake thread continues across turns through ADK session
state.

`SnowflakeCortexAgent` is experimental and lives under `google.adk.labs`. See
the
[SnowflakeCortexAgent guide](../../../../docs/guides/labs/snowflake/snowflake_cortex_agent/index.md)
for the full setup, limitations, and API details.

## Prerequisites

- A Cortex Agent object in Snowflake that the token below may run.
- A Snowflake token for the REST API: a programmatic access token, an OAuth
access token, or a key-pair JWT.
- Environment variables, in the shell or in a `.env` file next to `agent.py`
(`adk web` and `adk run` load it):

```text
SNOWFLAKE_ACCOUNT_URL=https://<account>.snowflakecomputing.com
SNOWFLAKE_DATABASE=SALES_DB
SNOWFLAKE_SCHEMA=ANALYTICS
SNOWFLAKE_CORTEX_AGENT=SALES_AGENT
SNOWFLAKE_TOKEN=<token>
SNOWFLAKE_TOKEN_TYPE=PROGRAMMATIC_ACCESS_TOKEN
```

`SNOWFLAKE_TOKEN_TYPE` is `PROGRAMMATIC_ACCESS_TOKEN`, `OAUTH`, `KEYPAIR_JWT` or
`WORKLOAD_IDENTITY_FEDERATION`, matching the token you supply.

No extra package is needed: the integration uses the `httpx` client ADK already
depends on.

## Sample Inputs

- `What were total sales by region last quarter?`

The Cortex Agent picks a semantic view, generates and runs SQL, and answers
from the result. The SQL tool call and its result appear as tool events, the
answer as the final event.

- `Show only the mobile channel.`

A follow-up in the same session continues the Snowflake thread, so the Cortex
Agent has the previous question and answer as context.

- `Which product categories exist in the data?`

A question the Cortex Agent can answer from the semantic model alone.

## Graph

The ADK agent fronts one Cortex Agent object, which owns its own tools:

```mermaid
graph LR
ADK[snowflake_cortex_analyst<br/>SnowflakeCortexAgent] -->|Run API| Cortex[SALES_AGENT<br/>Cortex Agent object]
Cortex --> Analyst(Cortex Analyst)
Cortex --> Search(Cortex Search)
Cortex --> SQL(SQL execution)
```

## How To

Point the agent at the Snowflake object and supply credentials through a header
provider:

```python
root_agent = SnowflakeCortexAgent(
name="snowflake_cortex_analyst",
description="Answers data questions by running a Snowflake Cortex Agent.",
account_url=_env("SNOWFLAKE_ACCOUNT_URL"),
database=_env("SNOWFLAKE_DATABASE"),
schema_name=_env("SNOWFLAKE_SCHEMA"),
cortex_agent_name=_env("SNOWFLAKE_CORTEX_AGENT"),
header_provider=snowflake_headers,
)
```

The header provider is a plain function that receives the invocation's
`ReadonlyContext` and returns the HTTP headers for one Snowflake request. This
sample reads one service token from the environment on every call, so a rotated
token is picked up without a restart. Missing settings are reported on the first
request rather than at import, so the sample can be listed by `adk web` before
it is configured:

```python
def snowflake_headers(ctx: ReadonlyContext) -> dict[str, str]:
_check_configured() # fails the first request, not the import
return {
"Authorization": f"Bearer {_env('SNOWFLAKE_TOKEN')}",
"X-Snowflake-Authorization-Token-Type": os.environ.get(
"SNOWFLAKE_TOKEN_TYPE", "PROGRAMMATIC_ACCESS_TOKEN"
),
}
```

Run it with `adk web contributing/samples/integrations` and pick
`snowflake_cortex_agent`, or with `adk run`. Use SSE streaming to see the text
and reasoning deltas as they arrive; without it only the tool events and the
final answer are yielded.

## Related Guides

- [SnowflakeCortexAgent](../../../../docs/guides/labs/snowflake/snowflake_cortex_agent/index.md) - Setup, event mapping, thread continuity, security and limitations of the Snowflake Cortex Agent integration.
76 changes: 76 additions & 0 deletions contributing/samples/integrations/snowflake_cortex_agent/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# Copyright 2026 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Analytics assistant that runs a Snowflake Cortex Agent as an ADK root agent.

Wraps an existing Cortex Agent object with `SnowflakeCortexAgent`. See the
guide at docs/guides/labs/snowflake/snowflake_cortex_agent/index.md for setup
and details.
"""

import os

from google.adk.agents.readonly_context import ReadonlyContext
from google.adk.labs.snowflake import SnowflakeCortexAgent

_REQUIRED_ENV = (
"SNOWFLAKE_ACCOUNT_URL",
"SNOWFLAKE_DATABASE",
"SNOWFLAKE_SCHEMA",
"SNOWFLAKE_CORTEX_AGENT",
"SNOWFLAKE_TOKEN",
)


def _env(name: str, default: str = "") -> str:
return os.environ.get(name, default).strip()


def _check_configured() -> None:
# Checked when the first request is made, not at import: `adk web` and the
# sample tests import every sample, with or without Snowflake credentials.
missing = [name for name in _REQUIRED_ENV if not _env(name)]
if missing:
raise RuntimeError(
"Snowflake settings are missing:"
f" {', '.join(missing)}. Set them in the environment or in a .env"
" file next to agent.py."
)


def snowflake_headers(ctx: ReadonlyContext) -> dict[str, str]:
"""Reads the token on every request so a rotated token is picked up."""
del ctx # One service token for every user; see the guide for per-user auth.
_check_configured()
return {
"Authorization": f"Bearer {_env('SNOWFLAKE_TOKEN')}",
"X-Snowflake-Authorization-Token-Type": _env(
"SNOWFLAKE_TOKEN_TYPE", "PROGRAMMATIC_ACCESS_TOKEN"
),
}


# 1. Point at the Cortex Agent object that already exists in Snowflake. The
# ADK name is separate from the Snowflake object name.
# 2. Credentials come from `header_provider`, never from a field, so they stay
# out of `repr`, the adk web agent graph and the session store.
root_agent = SnowflakeCortexAgent(
name="snowflake_cortex_analyst",
description="Answers data questions by running a Snowflake Cortex Agent.",
account_url=_env("SNOWFLAKE_ACCOUNT_URL"),
database=_env("SNOWFLAKE_DATABASE"),
schema_name=_env("SNOWFLAKE_SCHEMA"),
cortex_agent_name=_env("SNOWFLAKE_CORTEX_AGENT"),
header_provider=snowflake_headers,
)
1 change: 1 addition & 0 deletions docs/guides/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ This directory contains specific developer guides for the ADK Python implementat

### Labs
* [AntigravityAgent](labs/antigravity/index.md) - Runs a Google Antigravity SDK agent as an ADK agent node.
* [SnowflakeCortexAgent](labs/snowflake/snowflake_cortex_agent/index.md) - Runs a Snowflake Cortex Agent as an ADK root agent, streaming its run as ADK events.

### Live
* [LiveRequestQueue](live/live_request_queue/index.md) - Streaming content, realtime audio, and stream control signals to live agents.
Expand Down
Loading
Loading