Skip to content

Commit 5630cbb

Browse files
committed
Add ADK sql agent example
Add image describer demo without tool calls
1 parent 195e0ca commit 5630cbb

File tree

18 files changed

+1476
-1821
lines changed

18 files changed

+1476
-1821
lines changed

adk-sql-agent/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*.db

adk-sql-agent/.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.13

adk-sql-agent/README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# OpenTelemetry ADK instrumentation example
2+
3+
<!-- TODO: link to devsite doc once it is published -->
4+
5+
This sample is an ADK agent instrumented with OpenTelemetry to send traces and logs with GenAI
6+
prompts and responses, and metrics to Google Cloud Observability.
7+
8+
The Agent is a SQL expert that has full access to an ephemeral SQLite database. The database is
9+
initially empty.
10+
11+
## APIs and Permissions
12+
13+
Enable the relevant Cloud Observability APIs if they aren't already enabled.
14+
```sh
15+
gcloud services enable telemetry.googleapis.com logging.googleapis.com monitoring.googleapis.com cloudtrace.googleapis.com
16+
```
17+
18+
This sample writes to Cloud Logging, Cloud Monitoring, and Cloud Trace. Grant yourself the
19+
following roles to run the example:
20+
- `roles/logging.logWriter` – see https://cloud.google.com/logging/docs/access-control#permissions_and_roles
21+
- `roles/monitoring.metricWriter` – see https://cloud.google.com/monitoring/access-control#predefined_roles
22+
- `roles/telemetry.writer` – see https://cloud.google.com/trace/docs/iam#telemetry-roles
23+
24+
## Running the example
25+
26+
The sample can easily be run in Cloud Shell. You can also use
27+
[Application Default Credentials][ADC] locally. Clone and set environment variables:
28+
```sh
29+
git clone https://github.com/GoogleCloudPlatform/opentelemetry-operations-python.git
30+
cd opentelemetry-operations-python/samples/adk-sql-agent
31+
32+
# Capture GenAI prompts and responses
33+
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
34+
# Capture application logs automatically
35+
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
36+
```
37+
38+
Create a virtual environment and run the sample:
39+
```sh
40+
python -m venv venv/
41+
source venv/bin/activate
42+
pip install -r requirements.txt
43+
python main.py
44+
```
45+
46+
Alternatively if you have [`uv`](https://docs.astral.sh/uv/) installed:
47+
48+
```sh
49+
uv run main.py
50+
```
51+
52+
## Viewing the results
53+
54+
To view the generated traces with [Generative AI
55+
events](https://cloud.google.com/trace/docs/finding-traces#view_generative_ai_events) in the
56+
GCP console, use the [Trace Explorer](https://cloud.google.com/trace/docs/finding-traces). Filter for spans named `invoke agent`.
57+
58+
[ADC]: https://cloud.google.com/docs/authentication/application-default-credentials
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
GOOGLE_GENAI_USE_VERTEXAI=1
2+
GOOGLE_CLOUD_PROJECT=otel-starter-project
3+
GOOGLE_CLOUD_LOCATION=us-central1
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import agent
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
from textwrap import dedent
2+
3+
from google.adk.agents.llm_agent import Agent
4+
5+
root_agent = Agent(
6+
model="gemini-2.5-flash",
7+
name="root_agent",
8+
description="An agent that can describe images",
9+
instruction=dedent("""\
10+
You are an agent that can describe images. You don't have any tools at your disposal.
11+
Some guidelines for you:
12+
13+
- Don't answer any questions that are opinion based or involve emotions.
14+
- Keep it brief and succinct
15+
- But also be descriptive
16+
- Answer in a professional tone
17+
"""),
18+
)

adk-sql-agent/main.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
2+
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
3+
4+
GOOGLE_GENAI_USE_VERTEXAI=TRUE
5+
GOOGLE_CLOUD_LOCATION=us-central1
6+
7+
ENABLE_GCS_PYTHON_CLIENT_OTEL_TRACES=true

adk-sql-agent/main.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import logging
16+
import logging.handlers
17+
import os
18+
19+
import google.auth
20+
import google.auth.transport.requests
21+
import grpc
22+
import uvicorn
23+
from fastapi import FastAPI
24+
from google.adk.cli.fast_api import get_fast_api_app
25+
from google.auth.transport.grpc import AuthMetadataPlugin
26+
27+
from opentelemetry import _events as events
28+
from opentelemetry import _logs as logs
29+
from opentelemetry import metrics, trace
30+
from opentelemetry.exporter.cloud_logging import CloudLoggingExporter
31+
from opentelemetry.exporter.cloud_monitoring import (
32+
CloudMonitoringMetricsExporter,
33+
)
34+
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import (
35+
OTLPSpanExporter,
36+
)
37+
from opentelemetry.instrumentation.aiohttp_client import (
38+
AioHttpClientInstrumentor,
39+
)
40+
from opentelemetry.instrumentation.google_genai import (
41+
GoogleGenAiSdkInstrumentor,
42+
)
43+
from opentelemetry.instrumentation.google_genai.uploader import (
44+
GcsUploader,
45+
set_uploader,
46+
)
47+
from opentelemetry.instrumentation.sqlite3 import SQLite3Instrumentor
48+
from opentelemetry.instrumentation.vertexai import VertexAIInstrumentor
49+
from opentelemetry.sdk._events import EventLoggerProvider
50+
from opentelemetry.sdk._logs import LoggerProvider
51+
from opentelemetry.sdk._logs.export import (
52+
BatchLogRecordProcessor,
53+
ConsoleLogExporter,
54+
)
55+
from opentelemetry.sdk.metrics import MeterProvider
56+
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
57+
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
58+
from opentelemetry.sdk.trace import TracerProvider
59+
from opentelemetry.sdk.trace.export import BatchSpanProcessor
60+
61+
# Get the directory where main.py is located
62+
AGENT_DIR = os.path.dirname(os.path.abspath(__file__))
63+
# Example session DB URL (e.g., SQLite)
64+
SESSION_DB_URL = "sqlite:///./sessions.db"
65+
# Example allowed origins for CORS
66+
ALLOWED_ORIGINS = ["http://localhost", "http://localhost:8080", "*"]
67+
# Set web=True if you intend to serve a web interface, False otherwise
68+
SERVE_WEB_INTERFACE = True
69+
70+
logging.getLogger("opentelemetry").setLevel(logging.DEBUG)
71+
logging.getLogger("opentelemetry").addHandler(logging.StreamHandler())
72+
73+
74+
# [START opentelemetry_adk_otel_setup]
75+
def setup_opentelemetry() -> None:
76+
credentials, project_id = google.auth.default()
77+
resource = Resource.create(
78+
attributes={
79+
SERVICE_NAME: "adk-sql-agent",
80+
# The project to send spans to
81+
"gcp.project_id": project_id,
82+
}
83+
)
84+
85+
# Set up OTLP auth
86+
request = google.auth.transport.requests.Request()
87+
auth_metadata_plugin = AuthMetadataPlugin(
88+
credentials=credentials, request=request
89+
)
90+
channel_creds = grpc.composite_channel_credentials(
91+
grpc.ssl_channel_credentials(),
92+
grpc.metadata_call_credentials(auth_metadata_plugin),
93+
)
94+
95+
# Set up OpenTelemetry Python SDK
96+
tracer_provider = TracerProvider(resource=resource)
97+
tracer_provider.add_span_processor(
98+
BatchSpanProcessor(
99+
OTLPSpanExporter(
100+
credentials=channel_creds,
101+
endpoint="https://telemetry.googleapis.com:443/v1/traces",
102+
)
103+
)
104+
)
105+
trace.set_tracer_provider(tracer_provider)
106+
107+
logger_provider = LoggerProvider(resource=resource)
108+
logger_provider.add_log_record_processor(
109+
BatchLogRecordProcessor(CloudLoggingExporter())
110+
)
111+
# logger_provider.add_log_record_processor(
112+
# BatchLogRecordProcessor(
113+
# ConsoleLogExporter(out=open("./logs.jsonl", "w+"))
114+
# )
115+
# )
116+
logs.set_logger_provider(logger_provider)
117+
118+
event_logger_provider = EventLoggerProvider(logger_provider)
119+
events.set_event_logger_provider(event_logger_provider)
120+
121+
reader = PeriodicExportingMetricReader(CloudMonitoringMetricsExporter())
122+
meter_provider = MeterProvider(metric_readers=[reader], resource=resource)
123+
metrics.set_meter_provider(meter_provider)
124+
125+
# Set up prompt uploader
126+
# if OTEL_PYTHON_UPLOADER_PATH:
127+
# set_uploader(GcsUploader(bucket_path=OTEL_PYTHON_GCS_UPLOAD_BUCKET))
128+
129+
# Load instrumentors
130+
# SQLite3Instrumentor().instrument()
131+
VertexAIInstrumentor().instrument()
132+
GoogleGenAiSdkInstrumentor().instrument()
133+
AioHttpClientInstrumentor().instrument()
134+
135+
136+
# [END opentelemetry_adk_otel_setup]
137+
138+
139+
def main() -> None:
140+
# Make sure to set:
141+
# OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
142+
# OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
143+
# in order to full prompts and responses and logs messages.
144+
setup_opentelemetry()
145+
146+
# Call the function to get the FastAPI app instance
147+
# Ensure the agent directory name ('capital_agent') matches your agent folder
148+
app: FastAPI = get_fast_api_app(
149+
agents_dir=AGENT_DIR,
150+
session_service_uri=SESSION_DB_URL,
151+
allow_origins=ALLOWED_ORIGINS,
152+
web=SERVE_WEB_INTERFACE,
153+
)
154+
155+
uvicorn.run(
156+
app,
157+
host="0.0.0.0",
158+
port=int(os.environ.get("PORT", 8080)),
159+
)
160+
161+
162+
if __name__ == "__main__":
163+
main()

adk-sql-agent/pyproject.toml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# https://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
[project]
16+
name = "adk-sql-agent"
17+
version = "0.1.0"
18+
description = "An ADK agent that can run queries on an ephemeral SQLite database"
19+
readme = "README.md"
20+
requires-python = ">=3.13"
21+
dependencies = [
22+
"fsspec[gcs]>=2025.5.1",
23+
"google-adk>=1.0.0",
24+
"opentelemetry-exporter-gcp-logging>=1.9.0a0",
25+
"opentelemetry-exporter-gcp-monitoring>=1.9.0a0",
26+
"opentelemetry-exporter-otlp-proto-grpc>=1.33.1",
27+
"opentelemetry-instrumentation-aiohttp-client>=0.54b1",
28+
"opentelemetry-instrumentation-google-genai>=0.2b0",
29+
"opentelemetry-instrumentation-httpx>=0.54b1",
30+
"opentelemetry-instrumentation-requests>=0.54b1",
31+
"opentelemetry-instrumentation-sqlite3>=0.54b1",
32+
"opentelemetry-instrumentation-vertexai>=2.0b0",
33+
"rich>=14.0.0",
34+
]
35+
36+
[dependency-groups]
37+
dev = ["ipython>=9.3.0", "pillow>=11.2.1"]
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import agent
2+
3+
__all__ = ["agent"]

0 commit comments

Comments
 (0)